Merge remote-tracking branch 'origin/master'
# Conflicts: # store-service/src/main/java/com/justpickup/storeservice/domain/category/service/CategoryService.java # store-service/src/main/java/com/justpickup/storeservice/domain/category/web/CategoryOwnerApiController.java
This commit is contained in:
@@ -44,10 +44,10 @@ public class GlobalExceptionHandler implements ErrorWebExceptionHandler {
|
|||||||
exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
|
exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
|
||||||
responseBody.put("code", "INVALID");
|
responseBody.put("code", "INVALID");
|
||||||
responseBody.put("message", "Invalid Access Token");
|
responseBody.put("message", "Invalid Access Token");
|
||||||
}else{
|
} else {
|
||||||
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
|
exchange.getResponse().setStatusCode(exchange.getResponse().getStatusCode());
|
||||||
exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
|
exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
|
||||||
responseBody.put("code", "INVALID");
|
responseBody.put("code", ex.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
DataBuffer wrap = null;
|
DataBuffer wrap = null;
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ spring:
|
|||||||
- PUT
|
- PUT
|
||||||
- OPTIONS
|
- OPTIONS
|
||||||
- DELETE
|
- DELETE
|
||||||
|
- PATCH
|
||||||
allowedHeaders: '*'
|
allowedHeaders: '*'
|
||||||
allow-credentials: true
|
allow-credentials: true
|
||||||
routes:
|
routes:
|
||||||
@@ -57,6 +58,14 @@ spring:
|
|||||||
- AuthorizationHeaderFilter
|
- AuthorizationHeaderFilter
|
||||||
- RewritePath=/store-service/(?<segment>.*),/$\{segment}
|
- RewritePath=/store-service/(?<segment>.*),/$\{segment}
|
||||||
|
|
||||||
|
- id: notification-service
|
||||||
|
uri: lb://NOTIFICATION-SERVICE
|
||||||
|
predicates:
|
||||||
|
- Path=/notification-service/**
|
||||||
|
filters:
|
||||||
|
- AuthorizationHeaderFilter
|
||||||
|
- RewritePath=/notification-service/(?<segment>.*),/$\{segment}
|
||||||
|
|
||||||
- id: user-service
|
- id: user-service
|
||||||
uri: lb://USER-SERVICE
|
uri: lb://USER-SERVICE
|
||||||
predicates:
|
predicates:
|
||||||
|
|||||||
19
customer-vue/src/api/notification.js
Normal file
19
customer-vue/src/api/notification.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import axios from "axios";
|
||||||
|
|
||||||
|
const url = process.env.VUE_APP_CUSTOMER_SERVICE_BASEURL + "/notification-service";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
requestNotification() {
|
||||||
|
return axios.get(url + "/notifications");
|
||||||
|
},
|
||||||
|
patchNotification(id, isRead) {
|
||||||
|
const body = {
|
||||||
|
read: isRead
|
||||||
|
}
|
||||||
|
|
||||||
|
return axios.patch(url + "/notification/" + id, body)
|
||||||
|
},
|
||||||
|
countsNotification() {
|
||||||
|
return axios.get(url + "/api/notification/counts");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,56 +1,39 @@
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
requestNearbyStore(latitude, longitude, storeName, page, size) {
|
requestNearbyStore(latitude, longitude, storeName, page, size) {
|
||||||
const options = {
|
const options = {
|
||||||
params: {
|
params: {
|
||||||
latitude: latitude,
|
latitude: latitude,
|
||||||
longitude: longitude,
|
longitude: longitude,
|
||||||
storeName: storeName,
|
storeName: storeName,
|
||||||
page: page,
|
page: page,
|
||||||
size: size
|
size: size
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return axios.get(process.env.VUE_APP_STORE_API_URL + '/store/search', 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/');
|
getItemById(itemId){
|
||||||
},
|
return axios.get(process.env.VUE_APP_CUSTOMER_SERVICE_BASEURL+'/store-service/item/'+itemId)
|
||||||
putCategoryList(data){
|
},
|
||||||
return axios({
|
getFavoriteStore(latitude, longitude,){
|
||||||
method:'put',
|
const options = {
|
||||||
url:process.env.VUE_APP_CUSTOMER_SERVICE_BASEURL+'/store-service/category',
|
params: {
|
||||||
headers: {
|
latitude: latitude,
|
||||||
'Accept': 'application/json',
|
longitude: longitude,
|
||||||
'Content-Type': 'application/json;charset=UTF-8'
|
}
|
||||||
},
|
}
|
||||||
data: data,
|
return axios.get(process.env.VUE_APP_CUSTOMER_SERVICE_BASEURL+'/store-service/api/customer/store/favorite',options)
|
||||||
responseType:'json'
|
},
|
||||||
})
|
requestCategoriesWithItem(storeId) {
|
||||||
},
|
const options = {
|
||||||
getItemById(itemId){
|
params: {
|
||||||
return axios.get(process.env.VUE_APP_CUSTOMER_SERVICE_BASEURL+'/store-service/item/'+itemId)
|
"storeId": storeId
|
||||||
},
|
}
|
||||||
saveItem(method, itemData){
|
}
|
||||||
return axios({
|
return axios.get(process.env.VUE_APP_STORE_API_URL + "/categories", options);
|
||||||
method:method,
|
},
|
||||||
url: process.env.VUE_APP_CUSTOMER_SERVICE_BASEURL+'/store-service/item',
|
requestStore(storeId) {
|
||||||
headers: {
|
return axios.get(process.env.VUE_APP_CUSTOMER_SERVICE_BASEURL + "/store-service/store/" + storeId);
|
||||||
'Accept': 'application/json',
|
}
|
||||||
'Content-Type': 'application/json;charset=UTF-8'
|
|
||||||
},
|
|
||||||
data: itemData,
|
|
||||||
responseType:'json'
|
|
||||||
})
|
|
||||||
},
|
|
||||||
getFavoriteStore(latitude, longitude,){
|
|
||||||
const options = {
|
|
||||||
params: {
|
|
||||||
latitude: latitude,
|
|
||||||
longitude: longitude,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return axios.get(process.env.VUE_APP_CUSTOMER_SERVICE_BASEURL+'/store-service/api/customer/store/favorite',options)
|
|
||||||
},
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -13,18 +13,35 @@
|
|||||||
<v-img :src="require('@/assets/just-logo.png')"></v-img>
|
<v-img :src="require('@/assets/just-logo.png')"></v-img>
|
||||||
</v-toolbar-title>
|
</v-toolbar-title>
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
<v-btn icon>
|
|
||||||
<v-icon>mdi-magnify</v-icon>
|
<v-btn
|
||||||
</v-btn>
|
color="white"
|
||||||
|
elevation="0"
|
||||||
|
@click="goNotification"
|
||||||
|
>
|
||||||
|
<v-badge
|
||||||
|
:content="notificationCounts"
|
||||||
|
:value="notificationCounts"
|
||||||
|
color="orange"
|
||||||
|
overlap
|
||||||
|
>
|
||||||
|
<v-icon>mdi-bell-outline</v-icon>
|
||||||
|
</v-badge>
|
||||||
|
</v-btn>
|
||||||
</v-app-bar>
|
</v-app-bar>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: "AppNavigation"
|
name: "AppNavigation",
|
||||||
|
props: ["notificationCounts"],
|
||||||
|
methods: {
|
||||||
|
goNotification: function() {
|
||||||
|
this.$router.push('/notification');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
32
customer-vue/src/components/StoreNavigation.vue
Normal file
32
customer-vue/src/components/StoreNavigation.vue
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<template>
|
||||||
|
<v-app-bar
|
||||||
|
app
|
||||||
|
dense
|
||||||
|
color="white"
|
||||||
|
elevation="1"
|
||||||
|
hide-on-scroll
|
||||||
|
absolute
|
||||||
|
>
|
||||||
|
<v-app-bar-nav-icon>
|
||||||
|
<v-icon>mdi-arrow-left</v-icon>
|
||||||
|
</v-app-bar-nav-icon>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<v-toolbar-title>
|
||||||
|
<b>{{store.name}}</b>
|
||||||
|
</v-toolbar-title>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<v-btn icon>
|
||||||
|
<v-icon>mdi-magnify</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</v-app-bar>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "StoreNavigation",
|
||||||
|
props: ["store"],
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
</style>
|
||||||
@@ -4,6 +4,7 @@ import jwt from "@/common/jwt";
|
|||||||
import auth from "@/api/auth";
|
import auth from "@/api/auth";
|
||||||
|
|
||||||
import HomeLayout from '../views/Layout/HomeLayout.vue';
|
import HomeLayout from '../views/Layout/HomeLayout.vue';
|
||||||
|
import StoreLayout from "@/views/Layout/StoreLayout";
|
||||||
const ACCESS_TOKEN_NAME = "accessToken";
|
const ACCESS_TOKEN_NAME = "accessToken";
|
||||||
const EXPIRED_TIME_NAME = "expiredTime";
|
const EXPIRED_TIME_NAME = "expiredTime";
|
||||||
|
|
||||||
@@ -54,6 +55,11 @@ const routes = [
|
|||||||
name: 'favorite-store',
|
name: 'favorite-store',
|
||||||
component: () => import('../views/FavoriteStore')
|
component: () => import('../views/FavoriteStore')
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/notification",
|
||||||
|
name: 'notification',
|
||||||
|
component: () => import('../views/NotificationView')
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/login',
|
path: '/login',
|
||||||
name: 'login',
|
name: 'login',
|
||||||
@@ -61,7 +67,20 @@ const routes = [
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/store',
|
||||||
|
redirect: 'store',
|
||||||
|
beforeEnter: authCheck,
|
||||||
|
component: StoreLayout,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "/store/:storeId",
|
||||||
|
name: "store",
|
||||||
|
component: () => import('../views/StoreView'),
|
||||||
|
props: true
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/auth',
|
path: '/auth',
|
||||||
name: 'auth',
|
name: 'auth',
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-app>
|
<v-app>
|
||||||
<app-navigation></app-navigation>
|
<app-navigation
|
||||||
|
v-bind:notificationCounts="notificationCounts"></app-navigation>
|
||||||
<v-main>
|
<v-main>
|
||||||
<v-container class="px-8 py-8">
|
<v-container class="px-8 py-8">
|
||||||
<router-view></router-view>
|
<router-view
|
||||||
|
v-on:plusCount="notificationCounts++"
|
||||||
|
v-on:minusCount="notificationCounts--"></router-view>
|
||||||
</v-container>
|
</v-container>
|
||||||
</v-main>
|
</v-main>
|
||||||
<bottom-navigation></bottom-navigation>
|
<bottom-navigation></bottom-navigation>
|
||||||
@@ -13,12 +16,27 @@
|
|||||||
<script>
|
<script>
|
||||||
import AppNavigation from "../../components/AppNavigation.vue";
|
import AppNavigation from "../../components/AppNavigation.vue";
|
||||||
import BottomNavigation from "../../components/BottomNavigation.vue";
|
import BottomNavigation from "../../components/BottomNavigation.vue";
|
||||||
|
import notificationApi from "../../api/notification";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "HomeLayout",
|
name: "HomeLayout",
|
||||||
components: {
|
components: {
|
||||||
'app-navigation': AppNavigation,
|
'app-navigation': AppNavigation,
|
||||||
"bottom-navigation": BottomNavigation
|
"bottom-navigation": BottomNavigation
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.searchNotificationCounts();
|
||||||
|
},
|
||||||
|
data: function() {
|
||||||
|
return {
|
||||||
|
notificationCounts: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
searchNotificationCounts: async function() {
|
||||||
|
const response = await notificationApi.countsNotification();
|
||||||
|
this.notificationCounts = response.data.data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
62
customer-vue/src/views/Layout/StoreLayout.vue
Normal file
62
customer-vue/src/views/Layout/StoreLayout.vue
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
<template>
|
||||||
|
<v-app>
|
||||||
|
<store-navigation
|
||||||
|
v-bind:store="store">
|
||||||
|
</store-navigation>
|
||||||
|
<v-main>
|
||||||
|
<v-container class="px-8">
|
||||||
|
<router-view
|
||||||
|
v-on:getStoreId="renderNavigation">
|
||||||
|
</router-view>
|
||||||
|
</v-container>
|
||||||
|
</v-main>
|
||||||
|
<bottom-navigation></bottom-navigation>
|
||||||
|
</v-app>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import StoreNavigation from "../../components/StoreNavigation.vue";
|
||||||
|
import BottomNavigation from "../../components/BottomNavigation.vue";
|
||||||
|
|
||||||
|
import storeApi from "../../api/store";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "StoreLayout",
|
||||||
|
components: {
|
||||||
|
'store-navigation': StoreNavigation,
|
||||||
|
"bottom-navigation": BottomNavigation
|
||||||
|
},
|
||||||
|
data: () => ({
|
||||||
|
store: {
|
||||||
|
id: '',
|
||||||
|
name: ''
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
methods: {
|
||||||
|
renderNavigation: function(storeId) {
|
||||||
|
this.store.id = storeId;
|
||||||
|
this.getStore();
|
||||||
|
},
|
||||||
|
getStore: async function() {
|
||||||
|
if (!this.store.id) {
|
||||||
|
alert("매장 고유번호가 없습니다. 잠시후에 시도해주세요.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await storeApi.requestStore(this.store.id);
|
||||||
|
this.store = response.data.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.container {
|
||||||
|
max-width: 768px;
|
||||||
|
background-color: white;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
main {
|
||||||
|
background-color: #f2f2f2!important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
84
customer-vue/src/views/NotificationView.vue
Normal file
84
customer-vue/src/views/NotificationView.vue
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<template
|
||||||
|
v-for="(item, index) in notifications">
|
||||||
|
<v-list-item three-line :key="item.id">
|
||||||
|
<v-list-item-content>
|
||||||
|
<v-list-item-title>{{item.title}}</v-list-item-title>
|
||||||
|
<v-list-item-subtitle>
|
||||||
|
{{item.message}}
|
||||||
|
</v-list-item-subtitle>
|
||||||
|
<v-list-item-subtitle>
|
||||||
|
2022-03-09 14:00
|
||||||
|
</v-list-item-subtitle>
|
||||||
|
</v-list-item-content>
|
||||||
|
|
||||||
|
<v-list-item-action>
|
||||||
|
<v-checkbox
|
||||||
|
disabled
|
||||||
|
v-if="item.prevRead"
|
||||||
|
v-model="item.read"
|
||||||
|
hide-details></v-checkbox>
|
||||||
|
<v-checkbox
|
||||||
|
v-else
|
||||||
|
v-model="item.read"
|
||||||
|
@click="clickRead(item.id, item.read)"
|
||||||
|
hide-details></v-checkbox>
|
||||||
|
</v-list-item-action>
|
||||||
|
</v-list-item>
|
||||||
|
<v-divider
|
||||||
|
v-if="index < notifications.length - 1"
|
||||||
|
:key="index"
|
||||||
|
></v-divider>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import notificationApi from "@/api/notification";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "NotificationView",
|
||||||
|
mounted() {
|
||||||
|
this.search();
|
||||||
|
},
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
notifications: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
search: async function() {
|
||||||
|
const response = await notificationApi.requestNotification();
|
||||||
|
this.render(response.data);
|
||||||
|
},
|
||||||
|
render: function(json) {
|
||||||
|
const notifications = json.data.notifications;
|
||||||
|
notifications.forEach(notification => {
|
||||||
|
this.notifications.push({
|
||||||
|
id: notification.id,
|
||||||
|
message: notification.message,
|
||||||
|
title: notification.title,
|
||||||
|
prevRead: notification.read,
|
||||||
|
read: notification.read
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
clickRead: async function(id, isRead) {
|
||||||
|
await notificationApi.patchNotification(id, isRead);
|
||||||
|
|
||||||
|
if (isRead) {
|
||||||
|
alert("해당 알림은 읽음 처리되었습니다.");
|
||||||
|
this.$emit("minusCount");
|
||||||
|
} else {
|
||||||
|
alert("해당 알림은 읽음 해제 처리되었습니다.");
|
||||||
|
this.$emit("plusCount");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -26,7 +26,10 @@
|
|||||||
|
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col v-for="card in cards" v-bind:key="card.storeId" sm="6">
|
<v-col v-for="card in cards" v-bind:key="card.storeId" sm="6">
|
||||||
<v-card v-bind:data-id="card.storeId">
|
<v-card
|
||||||
|
v-bind:data-id="card.storeId"
|
||||||
|
v-on:click="clickCard(card.storeId)"
|
||||||
|
>
|
||||||
<v-img
|
<v-img
|
||||||
height="180"
|
height="180"
|
||||||
:src="require('@/assets/store.jpeg')"
|
:src="require('@/assets/store.jpeg')"
|
||||||
@@ -147,6 +150,12 @@ export default {
|
|||||||
favoriteCounts: store.favoriteCounts
|
favoriteCounts: store.favoriteCounts
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
clickCard(storeId) {
|
||||||
|
this.$router.push({
|
||||||
|
name: "store",
|
||||||
|
params: {storeId: storeId}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
111
customer-vue/src/views/StoreView.vue
Normal file
111
customer-vue/src/views/StoreView.vue
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div id="nav">
|
||||||
|
<v-chip-group
|
||||||
|
mandatory
|
||||||
|
center-active
|
||||||
|
show-arrows
|
||||||
|
active-class="primary--text"
|
||||||
|
v-model="tagIndex"
|
||||||
|
>
|
||||||
|
<v-chip
|
||||||
|
v-for="tag in tags"
|
||||||
|
:key="tag"
|
||||||
|
>
|
||||||
|
{{ tag }}
|
||||||
|
</v-chip>
|
||||||
|
</v-chip-group>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<div id="content">
|
||||||
|
<div v-for="category in categories" :key="category.id">
|
||||||
|
<h3 ref="focusTag">
|
||||||
|
{{ category.name }}
|
||||||
|
</h3>
|
||||||
|
<br>
|
||||||
|
<v-card
|
||||||
|
class="mx-auto mb-5"
|
||||||
|
outlined
|
||||||
|
v-for="item in category.items"
|
||||||
|
:key="item.id"
|
||||||
|
>
|
||||||
|
<v-list-item three-line>
|
||||||
|
<v-list-item-content>
|
||||||
|
<v-list-item-title class="text-h5 mb-3">
|
||||||
|
{{ item.name }}
|
||||||
|
</v-list-item-title>
|
||||||
|
<div class="text--primary mb-5">
|
||||||
|
{{ item.price }}원
|
||||||
|
</div>
|
||||||
|
<v-list-item-subtitle>
|
||||||
|
</v-list-item-subtitle>
|
||||||
|
</v-list-item-content>
|
||||||
|
|
||||||
|
<v-list-item-avatar
|
||||||
|
tile
|
||||||
|
size="100"
|
||||||
|
>
|
||||||
|
<v-img :src="require('@/assets/store.jpeg')"></v-img>
|
||||||
|
</v-list-item-avatar>
|
||||||
|
</v-list-item>
|
||||||
|
</v-card>
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import storeApi from "../api/store";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "StoreView",
|
||||||
|
props: ["storeId"],
|
||||||
|
mounted() {
|
||||||
|
if (!this.storeId) {
|
||||||
|
alert("잘못된 요청입니다.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.$emit('getStoreId', this.storeId)
|
||||||
|
this.search(this.storeId);
|
||||||
|
},
|
||||||
|
data: () => ({
|
||||||
|
tags: [],
|
||||||
|
categories: [],
|
||||||
|
tagIndex: 0
|
||||||
|
}),
|
||||||
|
watch: {
|
||||||
|
tagIndex: function(newValue) {
|
||||||
|
this.$refs.focusTag[newValue].scrollIntoView({behavior: 'smooth', block: 'center'});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
search: async function(storeId) {
|
||||||
|
const response = await storeApi.requestCategoriesWithItem(storeId);
|
||||||
|
this.render(response.data);
|
||||||
|
},
|
||||||
|
render: function(json) {
|
||||||
|
this.categories = json.data.categories;
|
||||||
|
this.categories.forEach(category => {
|
||||||
|
this.tags.push(category.name);
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
#nav {
|
||||||
|
color: white;
|
||||||
|
background-color: white;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 999;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content {
|
||||||
|
display: block !important;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -34,7 +34,6 @@ dependencies {
|
|||||||
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
|
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
|
||||||
implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'
|
implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'
|
||||||
/*implementation 'org.springframework.boot:spring-boot-starter-amqp'*/
|
/*implementation 'org.springframework.boot:spring-boot-starter-amqp'*/
|
||||||
/*implementation 'org.springframework.boot:spring-boot-starter-security'*/
|
|
||||||
/*implementation 'org.springframework.cloud:spring-cloud-starter-config'*/
|
/*implementation 'org.springframework.cloud:spring-cloud-starter-config'*/
|
||||||
/*implementation 'org.springframework.kafka:spring-kafka'*/
|
/*implementation 'org.springframework.kafka:spring-kafka'*/
|
||||||
// https://mvnrepository.com/artifact/com.github.gavlyukovskiy/p6spy-spring-boot-starter
|
// https://mvnrepository.com/artifact/com.github.gavlyukovskiy/p6spy-spring-boot-starter
|
||||||
|
|||||||
75
notification-service/src/docs/asciidoc/api-docs.adoc
Normal file
75
notification-service/src/docs/asciidoc/api-docs.adoc
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
:doctype: book
|
||||||
|
:icons: font
|
||||||
|
:source-highlighter: highlightjs
|
||||||
|
:toc: left
|
||||||
|
:toclevels: 2
|
||||||
|
:sectlinks:
|
||||||
|
|
||||||
|
|
||||||
|
[[overview]]
|
||||||
|
= 개요
|
||||||
|
|
||||||
|
[[overview-http-verbs]]
|
||||||
|
== HTTP 동사
|
||||||
|
|
||||||
|
본 REST API에서 사용하는 HTTP 동사(verbs)는 가능한한 표준 HTTP와 REST 규약을 따릅니다.
|
||||||
|
|
||||||
|
|===
|
||||||
|
| 동사 | 용례
|
||||||
|
|
||||||
|
| `GET`
|
||||||
|
| 리소스를 가져올 때 사용
|
||||||
|
|
||||||
|
| `POST`
|
||||||
|
| 새 리소스를 만들 때 사용
|
||||||
|
|
||||||
|
| `PUT`
|
||||||
|
| 기존 리소스를 수정할 때 사용
|
||||||
|
|
||||||
|
| `PATCH`
|
||||||
|
| 기존 리소스의 일부를 수정할 때 사용
|
||||||
|
|
||||||
|
| `DELETE`
|
||||||
|
| 기존 리소스를 삭제할 떄 사용
|
||||||
|
|===
|
||||||
|
|
||||||
|
[[overview-http-status-codes]]
|
||||||
|
== HTTP 상태 코드
|
||||||
|
|
||||||
|
본 REST API에서 사용하는 HTTP 상태 코드는 가능한 표준 HTTP와 REST 규약을 따릅니다.
|
||||||
|
|
||||||
|
|===
|
||||||
|
| 상태 코드 | 용례
|
||||||
|
|
||||||
|
| `200 OK`
|
||||||
|
| 요청을 성공적으로 처리함
|
||||||
|
|
||||||
|
| `201 Created`
|
||||||
|
| 새 리소스를 성공적으로 생성함. 응답의 `Location` 헤더에 해당 리소스의 URI가 담겨있다.
|
||||||
|
|
||||||
|
| `204 No Content`
|
||||||
|
| 기존 리소스를 성공적으로 수정함.
|
||||||
|
|
||||||
|
| `400 Bad Request`
|
||||||
|
| 잘못된 요청을 보낸 경우. 응답 본문에 더 오류에 대한 정보가 담겨있다.
|
||||||
|
|
||||||
|
| `404 Not Found`
|
||||||
|
| 요청한 리소스가 없음.
|
||||||
|
|
||||||
|
| `409 Conflict`
|
||||||
|
| 클라이언트의 요청이 서버의 상태와 충돌이 발생한 경우.
|
||||||
|
|===
|
||||||
|
|
||||||
|
[[snippets-write-convention]]
|
||||||
|
== snippets 작성 컨벤션
|
||||||
|
domain-httpRequestCode-etc
|
||||||
|
|
||||||
|
== 알림
|
||||||
|
=== 알림 조회 - 회원 고유번호
|
||||||
|
operation::notification-get[snippets='curl-request,http-request,http-response,request-headers,response-fields']
|
||||||
|
=== 알림 수정
|
||||||
|
operation::notification-patch[snippets='curl-request,http-request,http-response,path-parameters,request-fields,response-fields']
|
||||||
|
|
||||||
|
== API
|
||||||
|
=== 읽지 않은 알림 개수 조회
|
||||||
|
operation::api-get-notification-counts[snippets='curl-request,http-request,http-response,request-headers,response-fields']
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package com.justpickup.notificationservice.domain.notification.dto;
|
||||||
|
|
||||||
|
import com.justpickup.notificationservice.domain.notification.entity.Notification;
|
||||||
|
import com.justpickup.notificationservice.global.dto.Yn;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor(staticName = "of")
|
||||||
|
public class FindNotificationDto {
|
||||||
|
private Long id;
|
||||||
|
private Long userId;
|
||||||
|
private String message;
|
||||||
|
private String title;
|
||||||
|
private Yn readYn;
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
|
||||||
|
public FindNotificationDto(Notification entity) {
|
||||||
|
this.id = entity.getId();
|
||||||
|
this.userId = entity.getUserId();
|
||||||
|
this.message = entity.getMessage();
|
||||||
|
this.title = entity.getTitle();
|
||||||
|
this.readYn = entity.getReadYn();
|
||||||
|
this.createdAt = entity.getCreatedAt();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package com.justpickup.notificationservice.domain.notification.dto;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor(staticName = "of")
|
||||||
|
public class UpdateNotificationDto {
|
||||||
|
private Long id;
|
||||||
|
private boolean read;
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.justpickup.notificationservice.domain.notification.entity;
|
package com.justpickup.notificationservice.domain.notification.entity;
|
||||||
|
|
||||||
|
import com.justpickup.notificationservice.global.dto.Yn;
|
||||||
import com.justpickup.notificationservice.global.entity.BaseEntity;
|
import com.justpickup.notificationservice.global.entity.BaseEntity;
|
||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
@@ -13,7 +14,7 @@ import javax.persistence.*;
|
|||||||
@NoArgsConstructor(access = AccessLevel.PROTECTED)
|
@NoArgsConstructor(access = AccessLevel.PROTECTED)
|
||||||
public class Notification extends BaseEntity {
|
public class Notification extends BaseEntity {
|
||||||
|
|
||||||
@Id @GeneratedValue
|
@Id @GeneratedValue
|
||||||
@Column(name = "notification_id")
|
@Column(name = "notification_id")
|
||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
@@ -22,4 +23,24 @@ public class Notification extends BaseEntity {
|
|||||||
*/
|
*/
|
||||||
private Long userId;
|
private Long userId;
|
||||||
|
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
private Yn readYn;
|
||||||
|
|
||||||
|
// == 생성 메소드 == //
|
||||||
|
public static Notification of(Long userId, String message, String title) {
|
||||||
|
Notification notification = new Notification();
|
||||||
|
notification.userId = userId;
|
||||||
|
notification.message = message;
|
||||||
|
notification.title = title;
|
||||||
|
notification.readYn = Yn.N;
|
||||||
|
return notification;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void modifyReadYn(Yn readYn) {
|
||||||
|
this.readYn = readYn;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package com.justpickup.notificationservice.domain.notification.exception;
|
||||||
|
|
||||||
|
import com.justpickup.notificationservice.global.exception.CustomException;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
|
||||||
|
public class NotExistNotification extends CustomException {
|
||||||
|
|
||||||
|
public NotExistNotification(String message) {
|
||||||
|
super(HttpStatus.CONFLICT, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package com.justpickup.notificationservice.domain.notification.repository;
|
||||||
|
|
||||||
|
import com.justpickup.notificationservice.domain.notification.entity.Notification;
|
||||||
|
import com.justpickup.notificationservice.global.dto.Yn;
|
||||||
|
import org.springframework.data.domain.Sort;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface NotificationRepository extends JpaRepository<Notification, Long> {
|
||||||
|
List<Notification> findByUserId(Long userId, Sort sort);
|
||||||
|
long countByUserIdAndReadYn(Long userId, Yn readYn);
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package com.justpickup.notificationservice.domain.notification.service;
|
||||||
|
|
||||||
|
import com.justpickup.notificationservice.domain.notification.dto.FindNotificationDto;
|
||||||
|
import com.justpickup.notificationservice.domain.notification.dto.UpdateNotificationDto;
|
||||||
|
import com.justpickup.notificationservice.global.dto.Yn;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface NotificationService {
|
||||||
|
List<FindNotificationDto> findNotificationByUserId(Long id);
|
||||||
|
void updateNotification(UpdateNotificationDto dto);
|
||||||
|
Long findNotificationCounts(Long userId, Yn readYn);
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package com.justpickup.notificationservice.domain.notification.service;
|
||||||
|
|
||||||
|
import com.justpickup.notificationservice.domain.notification.dto.FindNotificationDto;
|
||||||
|
import com.justpickup.notificationservice.domain.notification.dto.UpdateNotificationDto;
|
||||||
|
import com.justpickup.notificationservice.domain.notification.entity.Notification;
|
||||||
|
import com.justpickup.notificationservice.domain.notification.exception.NotExistNotification;
|
||||||
|
import com.justpickup.notificationservice.domain.notification.repository.NotificationRepository;
|
||||||
|
import com.justpickup.notificationservice.global.dto.Yn;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.data.domain.Sort;
|
||||||
|
import org.springframework.data.domain.Sort.Order;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public class NotificationServiceImpl implements NotificationService {
|
||||||
|
|
||||||
|
private final NotificationRepository notificationRepository;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<FindNotificationDto> findNotificationByUserId(Long userId) {
|
||||||
|
Order readYnAsc = Order.asc("readYn");
|
||||||
|
Order createdAtDesc = Order.desc("createdAt");
|
||||||
|
|
||||||
|
Sort sort = Sort.by(List.of(readYnAsc, createdAtDesc));
|
||||||
|
return notificationRepository.findByUserId(userId, sort)
|
||||||
|
.stream()
|
||||||
|
.map(FindNotificationDto::new)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public void updateNotification(UpdateNotificationDto dto) {
|
||||||
|
Long id = dto.getId();
|
||||||
|
Notification notification = notificationRepository.findById(id)
|
||||||
|
.orElseThrow(() -> new NotExistNotification(id + "는 없는 알림 고유번호입니다."));
|
||||||
|
|
||||||
|
Yn readYn = dto.isRead() ? Yn.Y : Yn.N;
|
||||||
|
notification.modifyReadYn(readYn);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long findNotificationCounts(Long userId, Yn readYn) {
|
||||||
|
return notificationRepository.countByUserIdAndReadYn(userId, readYn);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package com.justpickup.notificationservice.domain.notification.web;
|
||||||
|
|
||||||
|
import com.justpickup.notificationservice.domain.notification.service.NotificationService;
|
||||||
|
import com.justpickup.notificationservice.global.dto.Result;
|
||||||
|
import com.justpickup.notificationservice.global.dto.Yn;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestHeader;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class NotificationApiController {
|
||||||
|
|
||||||
|
private final NotificationService notificationService;
|
||||||
|
|
||||||
|
@GetMapping("/notification/counts")
|
||||||
|
public ResponseEntity<Result> getNotificationCounts(@RequestHeader("user-id") String userIdHeader) {
|
||||||
|
|
||||||
|
Long userId = Long.valueOf(userIdHeader);
|
||||||
|
Yn readYn = Yn.N;
|
||||||
|
Long counts = notificationService.findNotificationCounts(userId, readYn);
|
||||||
|
|
||||||
|
return ResponseEntity.ok(Result.createSuccessResult(counts));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
package com.justpickup.notificationservice.domain.notification.web;
|
||||||
|
|
||||||
|
import com.justpickup.notificationservice.domain.notification.dto.FindNotificationDto;
|
||||||
|
import com.justpickup.notificationservice.domain.notification.dto.UpdateNotificationDto;
|
||||||
|
import com.justpickup.notificationservice.domain.notification.service.NotificationService;
|
||||||
|
import com.justpickup.notificationservice.global.dto.Result;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class NotificationController {
|
||||||
|
|
||||||
|
private final NotificationService notificationService;
|
||||||
|
|
||||||
|
@GetMapping("/notifications")
|
||||||
|
public ResponseEntity<Result> getNotificationByUserId(@RequestHeader("user-id") String userIdHeader) {
|
||||||
|
Long userId = Long.valueOf(userIdHeader);
|
||||||
|
|
||||||
|
List<FindNotificationDto> notifications = notificationService.findNotificationByUserId(userId);
|
||||||
|
|
||||||
|
GetNotificationResponse response = new GetNotificationResponse(notifications);
|
||||||
|
return ResponseEntity.ok(Result.createSuccessResult(response));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data @NoArgsConstructor
|
||||||
|
static class GetNotificationResponse {
|
||||||
|
private List<_Notification> notifications;
|
||||||
|
|
||||||
|
public GetNotificationResponse(List<FindNotificationDto> notifications) {
|
||||||
|
this.notifications =
|
||||||
|
notifications.stream().map(_Notification::new).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
static class _Notification {
|
||||||
|
private Long id;
|
||||||
|
private String message;
|
||||||
|
private String title;
|
||||||
|
private boolean read;
|
||||||
|
private String time;
|
||||||
|
|
||||||
|
public _Notification(FindNotificationDto dto) {
|
||||||
|
this.id = dto.getId();
|
||||||
|
this.message = dto.getMessage();
|
||||||
|
this.title = dto.getTitle();
|
||||||
|
this.read = dto.getReadYn().isY();
|
||||||
|
this.time = dto.getCreatedAt().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@PatchMapping("/notification/{notificationId}")
|
||||||
|
public ResponseEntity<Result> patchNotification(@PathVariable("notificationId") Long notificationId,
|
||||||
|
@RequestBody PatchNotificationRequest notificationRequest) {
|
||||||
|
|
||||||
|
UpdateNotificationDto dto = UpdateNotificationDto.of(notificationId, notificationRequest.isRead());
|
||||||
|
|
||||||
|
notificationService.updateNotification(dto);
|
||||||
|
|
||||||
|
return ResponseEntity.ok(Result.createSuccessResult(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data @NoArgsConstructor @AllArgsConstructor
|
||||||
|
static class PatchNotificationRequest {
|
||||||
|
private boolean read;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package com.justpickup.notificationservice.global;
|
||||||
|
|
||||||
|
import com.justpickup.notificationservice.domain.notification.entity.Notification;
|
||||||
|
import com.justpickup.notificationservice.domain.notification.repository.NotificationRepository;
|
||||||
|
import com.justpickup.notificationservice.global.dto.Yn;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.boot.CommandLineRunner;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class SqlCommandLineRunner implements CommandLineRunner {
|
||||||
|
|
||||||
|
private final NotificationRepository notificationRepository;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public void run(String... args) throws Exception {
|
||||||
|
for (long userId = 1; userId < 10; userId++) {
|
||||||
|
List<Notification> notifications = new ArrayList<>();
|
||||||
|
for (int notification = 1; notification <= 5; notification++) {
|
||||||
|
Notification of = Notification.of(userId, notification + "번 매장의 주문이 수락되었습니다.", "주문이 수락되었어요");
|
||||||
|
notifications.add(of);
|
||||||
|
}
|
||||||
|
for (int notification = 6; notification <= 10; notification++) {
|
||||||
|
Notification of = Notification.of(userId, notification + "번 매장의 주문이 수락되었습니다.", "주문이 수락되었어요");
|
||||||
|
of.modifyReadYn(Yn.Y);
|
||||||
|
notifications.add(of);
|
||||||
|
}
|
||||||
|
notificationRepository.saveAll(notifications);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package com.justpickup.notificationservice.global.dto;
|
||||||
|
|
||||||
|
public enum Code {
|
||||||
|
SUCCESS, ERROR
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package com.justpickup.notificationservice.global.dto;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data @NoArgsConstructor
|
||||||
|
public class Result<T> {
|
||||||
|
private Code code;
|
||||||
|
private String message;
|
||||||
|
private T data;
|
||||||
|
|
||||||
|
@Builder
|
||||||
|
public Result(Code code, String message, T data) {
|
||||||
|
this.code = code;
|
||||||
|
this.message = message;
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Result createErrorResult(String message) {
|
||||||
|
return Result.builder()
|
||||||
|
.code(Code.ERROR)
|
||||||
|
.message(message)
|
||||||
|
.data(null)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 해당 <T> 는 클래스의 T와 다름
|
||||||
|
public static <T> Result createSuccessResult(T data) {
|
||||||
|
return Result.builder()
|
||||||
|
.code(Code.SUCCESS)
|
||||||
|
.message("")
|
||||||
|
.data(data)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Result success(){
|
||||||
|
return Result.builder()
|
||||||
|
.code(Code.SUCCESS)
|
||||||
|
.message("성공")
|
||||||
|
.data(null)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package com.justpickup.notificationservice.global.dto;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public enum Yn {
|
||||||
|
Y(true), N(false);
|
||||||
|
|
||||||
|
private boolean y;
|
||||||
|
|
||||||
|
Yn(boolean y) {
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,14 +1,21 @@
|
|||||||
package com.justpickup.notificationservice.global.entity;
|
package com.justpickup.notificationservice.global.entity;
|
||||||
|
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.hibernate.annotations.CreationTimestamp;
|
||||||
|
import org.hibernate.annotations.UpdateTimestamp;
|
||||||
|
|
||||||
import javax.persistence.MappedSuperclass;
|
import javax.persistence.MappedSuperclass;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
@MappedSuperclass
|
@MappedSuperclass
|
||||||
|
@Getter
|
||||||
public abstract class BaseEntity {
|
public abstract class BaseEntity {
|
||||||
|
|
||||||
|
@CreationTimestamp
|
||||||
private LocalDateTime createdAt;
|
private LocalDateTime createdAt;
|
||||||
private Long createdBy;
|
private Long createdBy;
|
||||||
|
@UpdateTimestamp
|
||||||
private LocalDateTime lastModifiedAt;
|
private LocalDateTime lastModifiedAt;
|
||||||
private Long lastModifiedBy;
|
private Long lastModifiedBy;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.justpickup.notificationservice.global.exception;
|
||||||
|
|
||||||
|
import com.justpickup.notificationservice.global.dto.Result;
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public class CustomException extends RuntimeException {
|
||||||
|
|
||||||
|
private HttpStatus status;
|
||||||
|
private Result errorResult;
|
||||||
|
|
||||||
|
protected CustomException(HttpStatus status, String message) {
|
||||||
|
this.status = status;
|
||||||
|
this.errorResult = Result.createErrorResult(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package com.justpickup.notificationservice.global.exception;
|
||||||
|
|
||||||
|
import com.justpickup.notificationservice.global.dto.Result;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
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.MethodArgumentNotValidException;
|
||||||
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
|
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||||
|
|
||||||
|
@RestControllerAdvice
|
||||||
|
@Slf4j
|
||||||
|
public class GlobalExceptionHandler {
|
||||||
|
|
||||||
|
@ExceptionHandler(CustomException.class)
|
||||||
|
public ResponseEntity customExceptionHandler(CustomException ce) {
|
||||||
|
HttpStatus status = ce.getStatus();
|
||||||
|
Result errorResult = ce.getErrorResult();
|
||||||
|
|
||||||
|
return ResponseEntity.status(status)
|
||||||
|
.body(errorResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(BindException.class)
|
||||||
|
public ResponseEntity bindExceptionHandler(BindException exception) {
|
||||||
|
return getValidationErrorBody(exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||||
|
public ResponseEntity methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException exception) {
|
||||||
|
return getValidationErrorBody(exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResponseEntity getValidationErrorBody(BindException exception) {
|
||||||
|
BindingResult bindingResult = exception.getBindingResult();
|
||||||
|
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
bindingResult.getFieldErrors()
|
||||||
|
.forEach(fieldError -> {
|
||||||
|
builder.append("[");
|
||||||
|
builder.append(fieldError.getField());
|
||||||
|
builder.append("](은)는 ");
|
||||||
|
builder.append(fieldError.getDefaultMessage());
|
||||||
|
builder.append(" 입력된 값: [");
|
||||||
|
builder.append(fieldError.getRejectedValue());
|
||||||
|
builder.append("]");
|
||||||
|
});
|
||||||
|
|
||||||
|
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
|
||||||
|
.body(Result.createErrorResult(builder.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.justpickup.notificationservice.config;
|
||||||
|
|
||||||
|
import org.springframework.boot.test.autoconfigure.restdocs.RestDocsMockMvcConfigurationCustomizer;
|
||||||
|
import org.springframework.boot.test.context.TestConfiguration;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
|
||||||
|
import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint;
|
||||||
|
|
||||||
|
@TestConfiguration
|
||||||
|
public class TestConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public RestDocsMockMvcConfigurationCustomizer restDocsMockMvcConfigurationCustomizer() {
|
||||||
|
return configurer -> configurer.operationPreprocessors()
|
||||||
|
.withRequestDefaults(prettyPrint())
|
||||||
|
.withResponseDefaults(prettyPrint());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package com.justpickup.notificationservice.domain.notification.web;
|
||||||
|
|
||||||
|
import com.justpickup.notificationservice.config.TestConfig;
|
||||||
|
import com.justpickup.notificationservice.domain.notification.service.NotificationService;
|
||||||
|
import com.justpickup.notificationservice.global.dto.Yn;
|
||||||
|
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.test.web.servlet.MockMvc;
|
||||||
|
import org.springframework.test.web.servlet.ResultActions;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
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.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.test.web.servlet.result.MockMvcResultHandlers.print;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
|
@WebMvcTest(NotificationApiController.class)
|
||||||
|
@Import(TestConfig.class)
|
||||||
|
@AutoConfigureRestDocs(uriHost = "https://just-pickup.com", uriPort = 8000)
|
||||||
|
class NotificationApiControllerTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
MockMvc mockMvc;
|
||||||
|
|
||||||
|
@MockBean
|
||||||
|
NotificationService notificationService;
|
||||||
|
|
||||||
|
private final String url = "/api/";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("[API] [GET] 읽지 않은 알림 개수 가져오기")
|
||||||
|
void getNotificationCounts() throws Exception {
|
||||||
|
// GIVEN
|
||||||
|
Long userId = 1L;
|
||||||
|
given(notificationService.findNotificationCounts(userId, Yn.N))
|
||||||
|
.willReturn(10L);
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
ResultActions actions = mockMvc.perform(get(url + "/notification/counts")
|
||||||
|
.header("user-id", String.valueOf(userId)));
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
actions.andExpect(status().isOk())
|
||||||
|
.andDo(print())
|
||||||
|
.andDo(document("api-get-notification-counts",
|
||||||
|
requestHeaders(
|
||||||
|
headerWithName("user-id").description("회원 고유번호")
|
||||||
|
),
|
||||||
|
responseFields(
|
||||||
|
fieldWithPath("code").description("결과코드 SUCCESS/ERROR"),
|
||||||
|
fieldWithPath("message").description("메시지"),
|
||||||
|
fieldWithPath("data").description("알림 개수")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
package com.justpickup.notificationservice.domain.notification.web;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.justpickup.notificationservice.config.TestConfig;
|
||||||
|
import com.justpickup.notificationservice.domain.notification.dto.FindNotificationDto;
|
||||||
|
import com.justpickup.notificationservice.domain.notification.service.NotificationService;
|
||||||
|
import com.justpickup.notificationservice.domain.notification.web.NotificationController.PatchNotificationRequest;
|
||||||
|
import com.justpickup.notificationservice.global.dto.Code;
|
||||||
|
import com.justpickup.notificationservice.global.dto.Yn;
|
||||||
|
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.http.MediaType;
|
||||||
|
import org.springframework.restdocs.request.RequestDocumentation;
|
||||||
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
import org.springframework.test.web.servlet.ResultActions;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
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.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
|
||||||
|
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.patch;
|
||||||
|
import static org.springframework.restdocs.payload.PayloadDocumentation.*;
|
||||||
|
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
|
||||||
|
import static org.springframework.restdocs.request.RequestDocumentation.pathParameters;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
|
@WebMvcTest(NotificationController.class)
|
||||||
|
@Import(TestConfig.class)
|
||||||
|
@AutoConfigureRestDocs(uriHost = "https://just-pickup.com", uriPort = 8000)
|
||||||
|
class NotificationControllerTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
MockMvc mockMvc;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
@MockBean
|
||||||
|
NotificationService notificationService;
|
||||||
|
|
||||||
|
private final String url = "/notification";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("[GET] 회원 고유번호로 알림 가져오기")
|
||||||
|
void getNotificationByUserId() throws Exception {
|
||||||
|
// GIVEN
|
||||||
|
long userId = 1L;
|
||||||
|
given(notificationService.findNotificationByUserId(userId))
|
||||||
|
.willReturn(getNotificationByUserIdWillReturn(userId));
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
ResultActions actions
|
||||||
|
= mockMvc.perform(get(url).header("user-id", String.valueOf(userId)));
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
actions.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("code").value(Code.SUCCESS.name()))
|
||||||
|
.andExpect(jsonPath("message").value(""))
|
||||||
|
.andExpect(jsonPath("data").isNotEmpty())
|
||||||
|
.andDo(print())
|
||||||
|
.andDo(document("notification-get",
|
||||||
|
requestHeaders(
|
||||||
|
headerWithName("user-id").description("회원 고유번호")
|
||||||
|
),
|
||||||
|
responseFields(
|
||||||
|
fieldWithPath("code").description("결과코드 SUCCESS/ERROR"),
|
||||||
|
fieldWithPath("message").description("메시지"),
|
||||||
|
fieldWithPath("data.notifications[*].id").description("알림 고유번호"),
|
||||||
|
fieldWithPath("data.notifications[*].message").description("알림 내용"),
|
||||||
|
fieldWithPath("data.notifications[*].title").description("알림 제목"),
|
||||||
|
fieldWithPath("data.notifications[*].read").description("알림 읽음 여부"),
|
||||||
|
fieldWithPath("data.notifications[*].time").description("알림 생성 시간 [YYYY-MM-DD HH:MM]")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<FindNotificationDto> getNotificationByUserIdWillReturn(long userId) {
|
||||||
|
List<FindNotificationDto> returnList = new ArrayList<>();
|
||||||
|
for (long id = 1; id <= 5; id++) {
|
||||||
|
returnList.add(FindNotificationDto.of(id, userId, id + "번 메시지 예시입니다.", "제목" + id, Yn.Y, LocalDateTime.now()));
|
||||||
|
}
|
||||||
|
for (long id = 6; id <= 10; id++) {
|
||||||
|
returnList.add(FindNotificationDto.of(id, userId, id + "번 메시지 예시입니다.", "제목" + id, Yn.N, LocalDateTime.now()));
|
||||||
|
}
|
||||||
|
return returnList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("[API] 알림 수정")
|
||||||
|
void patchNotification() throws Exception {
|
||||||
|
// GIVEN
|
||||||
|
long notificationId = 1L;
|
||||||
|
PatchNotificationRequest request = new PatchNotificationRequest(true);
|
||||||
|
String requestBody = objectMapper.writeValueAsString(request);
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
ResultActions actions = mockMvc.perform(
|
||||||
|
patch(url + "/{notificationId}", String.valueOf(notificationId))
|
||||||
|
.content(requestBody)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.accept(MediaType.APPLICATION_JSON)
|
||||||
|
);
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
actions.andExpect(status().isOk())
|
||||||
|
.andDo(print())
|
||||||
|
.andDo(document("notification-patch",
|
||||||
|
pathParameters(
|
||||||
|
parameterWithName("notificationId").description("알림 고유번호")
|
||||||
|
),
|
||||||
|
requestFields(
|
||||||
|
fieldWithPath("read").description("읽음 여부")
|
||||||
|
),
|
||||||
|
responseFields(
|
||||||
|
fieldWithPath("code").description("결과코드 SUCCESS/ERROR"),
|
||||||
|
fieldWithPath("message").description("메시지"),
|
||||||
|
fieldWithPath("data").description("데이터")
|
||||||
|
)
|
||||||
|
))
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -95,8 +95,8 @@ operation::put-categoryList[snippets='curl-request,http-request,http-response,re
|
|||||||
== 매장
|
== 매장
|
||||||
=== 매장 검색 조회
|
=== 매장 검색 조회
|
||||||
operation::api-customer-store-search[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']
|
operation::favoriteStore-get[snippets='curl-request,http-request,http-response,request-headers,request-parameters,response-fields']
|
||||||
|
=== 매장 조회
|
||||||
|
operation::store-get[snippets='curl-request,http-request,http-response,path-parameters,response-fields']
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,8 @@
|
|||||||
package com.justpickup.storeservice;
|
package com.justpickup.storeservice;
|
||||||
|
|
||||||
import com.justpickup.storeservice.domain.favoritestore.entity.FavoriteStore;
|
|
||||||
import com.justpickup.storeservice.domain.favoritestore.repository.FavoriteStoreRepository;
|
|
||||||
import com.justpickup.storeservice.domain.map.entity.Map;
|
|
||||||
import com.justpickup.storeservice.domain.store.entity.Store;
|
|
||||||
import com.justpickup.storeservice.domain.store.repository.StoreRepository;
|
|
||||||
import com.justpickup.storeservice.global.entity.Address;
|
|
||||||
import org.springframework.boot.CommandLineRunner;
|
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
|
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@EnableEurekaClient
|
@EnableEurekaClient
|
||||||
@@ -23,56 +12,4 @@ public class StoreServiceApplication {
|
|||||||
SpringApplication.run(StoreServiceApplication.class, args);
|
SpringApplication.run(StoreServiceApplication.class, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
|
||||||
CommandLineRunner run(StoreRepository storeRepository, FavoriteStoreRepository favoriteStoreRepository) {
|
|
||||||
return args -> {
|
|
||||||
List<Store> stores = new ArrayList<>();
|
|
||||||
|
|
||||||
stores.add(
|
|
||||||
Store.of(
|
|
||||||
new Address("서울시", "마포구 도화동", "201-20"),
|
|
||||||
Map.of(37.5398271003404, 126.94769672415691),
|
|
||||||
1L,
|
|
||||||
"커피온리 마포역점"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
stores.add(
|
|
||||||
Store.of(
|
|
||||||
new Address("서울시", "마포구 도화동", "50-10"),
|
|
||||||
Map.of(37.54010719003089, 126.94556661330861),
|
|
||||||
2L,
|
|
||||||
"만랩커피 마포점"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
stores.add(
|
|
||||||
Store.of(
|
|
||||||
new Address("서울시", "마포구 도화동", "555"),
|
|
||||||
Map.of(37.539797393793755, 126.9453578838543),
|
|
||||||
3L,
|
|
||||||
"이디야커피 마포오벨리스크점"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
stores.add(
|
|
||||||
Store.of(
|
|
||||||
new Address("서울시", "영등포구 도림로", "31길 2"),
|
|
||||||
Map.of(37.493033141569505, 126.89593667847592),
|
|
||||||
4L,
|
|
||||||
"이디야커피 대림역점"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
storeRepository.saveAll(stores);
|
|
||||||
|
|
||||||
List<Long> userList = List.of(1L,2L,3L,4L,5L,6L,7L);
|
|
||||||
userList.forEach(userId -> {
|
|
||||||
stores.forEach(store -> {
|
|
||||||
favoriteStoreRepository.save(FavoriteStore.of(userId, store));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,4 +60,11 @@ public class Category extends BaseEntity {
|
|||||||
return new Category(id,name,order,store);
|
return new Category(id,name,order,store);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Category of(String name, Integer order, Store store) {
|
||||||
|
Category category = new Category();
|
||||||
|
category.name = name;
|
||||||
|
category.order = order;
|
||||||
|
category.store = store;
|
||||||
|
return category;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import com.justpickup.storeservice.domain.category.repository.CategoryRepository
|
|||||||
import com.justpickup.storeservice.domain.category.repository.CategoryRepositoryCustom;
|
import com.justpickup.storeservice.domain.category.repository.CategoryRepositoryCustom;
|
||||||
import com.justpickup.storeservice.domain.store.entity.Store;
|
import com.justpickup.storeservice.domain.store.entity.Store;
|
||||||
import com.justpickup.storeservice.domain.store.repository.StoreRepository;
|
import com.justpickup.storeservice.domain.store.repository.StoreRepository;
|
||||||
import com.justpickup.storeservice.global.exception.CustomException;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
@@ -27,9 +26,9 @@ public class CategoryService {
|
|||||||
private final CategoryRepositoryCustom categoryRepositoryCustom;
|
private final CategoryRepositoryCustom categoryRepositoryCustom;
|
||||||
private final StoreRepository storeRepository;
|
private final StoreRepository storeRepository;
|
||||||
|
|
||||||
public List<CategoryDto> getCategoryList(Long userId){
|
public List<CategoryDto> getCategoriesWithItem(Long storeId){
|
||||||
|
|
||||||
return categoryRepositoryCustom.getCategoryList(userId)
|
return categoryRepositoryCustom.getCategoryList(storeId)
|
||||||
.stream()
|
.stream()
|
||||||
.map(CategoryDto::new)
|
.map(CategoryDto::new)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
@@ -74,6 +73,6 @@ public class CategoryService {
|
|||||||
.orElseThrow(() -> new NotFoundStoreException(HttpStatus.BAD_REQUEST,"존재하지않는 Category")));
|
.orElseThrow(() -> new NotFoundStoreException(HttpStatus.BAD_REQUEST,"존재하지않는 Category")));
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
package com.justpickup.storeservice.domain.category.web;
|
||||||
|
|
||||||
|
import com.justpickup.storeservice.domain.category.dto.CategoryDto;
|
||||||
|
import com.justpickup.storeservice.domain.category.service.CategoryService;
|
||||||
|
import com.justpickup.storeservice.global.dto.Result;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
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.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RequestMapping("/api/customer/")
|
||||||
|
public class CategoryCustomerApiController {
|
||||||
|
|
||||||
|
private final CategoryService categoryService;
|
||||||
|
|
||||||
|
@GetMapping("/categories")
|
||||||
|
public ResponseEntity<Result> getCategories(@RequestParam("storeId") Long storeId) {
|
||||||
|
List<CategoryDto> categoryList = categoryService.getCategoriesWithItem(storeId);
|
||||||
|
|
||||||
|
GetCategoriesResponse getCategoriesResponse = new GetCategoriesResponse(categoryList);
|
||||||
|
|
||||||
|
return ResponseEntity.ok(Result.createSuccessResult(getCategoriesResponse));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data @NoArgsConstructor
|
||||||
|
static class GetCategoriesResponse {
|
||||||
|
private List<_Category> categories;
|
||||||
|
|
||||||
|
public GetCategoriesResponse(List<CategoryDto> categoryDtoList) {
|
||||||
|
this. categories = categoryDtoList
|
||||||
|
.stream()
|
||||||
|
.map(_Category::new)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
static class _Category {
|
||||||
|
private Long id;
|
||||||
|
private String name;
|
||||||
|
private Integer order;
|
||||||
|
private List<_Item> items;
|
||||||
|
|
||||||
|
public _Category(CategoryDto categoryDto) {
|
||||||
|
List<_Item> items = categoryDto.getItems()
|
||||||
|
.stream()
|
||||||
|
.map(itemDto -> new _Item(itemDto.getId(), itemDto.getName(), itemDto.getPrice()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
this.id = categoryDto.getId();
|
||||||
|
this.name = categoryDto.getName();
|
||||||
|
this.order = categoryDto.getOrder();
|
||||||
|
this.items = items;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
static class _Item {
|
||||||
|
private Long id;
|
||||||
|
private String name;
|
||||||
|
private Long price;
|
||||||
|
|
||||||
|
public _Item(Long id, String name, Long price) {
|
||||||
|
this.id = id;
|
||||||
|
this.name = name;
|
||||||
|
this.price = price;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,7 +23,8 @@ public class CategoryOwnerApiController {
|
|||||||
|
|
||||||
@GetMapping("/category")
|
@GetMapping("/category")
|
||||||
public ResponseEntity getCategoryList(@RequestHeader(value = "user-id") String userId ){
|
public ResponseEntity getCategoryList(@RequestHeader(value = "user-id") String userId ){
|
||||||
List<CategoryDto> categoryList = categoryService.getCategoryList(Long.parseLong(userId));
|
Long storeId = Long.parseLong(userId);
|
||||||
|
List<CategoryDto> categoryList = categoryService.getCategoriesWithItem(storeId);
|
||||||
|
|
||||||
List<CategoryResponse> categoryResponseList = categoryList.stream()
|
List<CategoryResponse> categoryResponseList = categoryList.stream()
|
||||||
.map(CategoryResponse::new)
|
.map(CategoryResponse::new)
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ public class ItemDto {
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ItemDto createWithCategoryItemDto(Item item) {
|
public static ItemDto createWithCategory(Item item) {
|
||||||
return ItemDto.builder()
|
return ItemDto.builder()
|
||||||
.id(item.getId())
|
.id(item.getId())
|
||||||
.name(item.getName())
|
.name(item.getName())
|
||||||
@@ -79,9 +79,4 @@ public class ItemDto {
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: 2022/02/03 queryDsl 쿼리 생성 시 구현 필요
|
|
||||||
// public static ItemDto createFullItemDto(Item item) {
|
|
||||||
// return null
|
|
||||||
// }
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,11 +11,9 @@ import lombok.Getter;
|
|||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
import javax.persistence.*;
|
import javax.persistence.*;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static javax.persistence.CascadeType.PERSIST;
|
|
||||||
import static javax.persistence.CascadeType.REMOVE;
|
|
||||||
import static javax.persistence.FetchType.LAZY;
|
import static javax.persistence.FetchType.LAZY;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@@ -45,8 +43,8 @@ public class Item extends BaseEntity {
|
|||||||
@JoinColumn(name = "store_id")
|
@JoinColumn(name = "store_id")
|
||||||
private Store store;
|
private Store store;
|
||||||
|
|
||||||
@OneToMany(mappedBy = "item" ,cascade = {REMOVE,PERSIST} )
|
@OneToMany(mappedBy = "item", cascade = CascadeType.ALL)
|
||||||
private List<ItemOption> itemOptions;
|
private List<ItemOption> itemOptions = new ArrayList<>();
|
||||||
|
|
||||||
// == 연관관계 편의 메소드 ==//
|
// == 연관관계 편의 메소드 ==//
|
||||||
public void addItemOption(ItemOption itemOption) {
|
public void addItemOption(ItemOption itemOption) {
|
||||||
@@ -71,19 +69,15 @@ public class Item extends BaseEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// == 생성 메소드 == //
|
// == 생성 메소드 == //
|
||||||
public static Item createdItem(Category category, Store store, List<ItemOption> itemOptions) {
|
public static Item of(String name, Long price, Category category, Store store, List<ItemOption> itemOptions) {
|
||||||
Item item = new Item();
|
Item item = new Item();
|
||||||
item.setCategory(category);
|
item.name = name;
|
||||||
item.setStore(store);
|
item.price = price;
|
||||||
itemOptions.forEach(item::addItemOption);
|
item.category = category;
|
||||||
return item;
|
item.store = store;
|
||||||
}
|
for (ItemOption itemOption : itemOptions) {
|
||||||
|
item.addItemOption(itemOption);
|
||||||
public static Item createdFullItem(Category category, Store store, List<ItemOption> itemOptions, String name , Long price) {
|
}
|
||||||
Item item = new Item();
|
|
||||||
item.setItemNameAndPriceAndCategory( name, price ,category);
|
|
||||||
item.setStore(store);
|
|
||||||
itemOptions.forEach(item::addItemOption);
|
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,8 @@
|
|||||||
package com.justpickup.storeservice.domain.item.repository;
|
package com.justpickup.storeservice.domain.item.repository;
|
||||||
|
|
||||||
import com.justpickup.storeservice.domain.item.entity.Item;
|
import com.justpickup.storeservice.domain.item.entity.Item;
|
||||||
import com.justpickup.storeservice.domain.store.entity.Store;
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public interface ItemRepository extends JpaRepository<Item, Long> {
|
public interface ItemRepository extends JpaRepository<Item, Long> {
|
||||||
|
|
||||||
List<Item> findByStore(Store store);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,9 +20,7 @@ import org.springframework.data.support.PageableExecutionUtils;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@@ -61,7 +59,7 @@ public class ItemServiceImpl implements ItemService {
|
|||||||
|
|
||||||
Page<Item> itemList = itemRepositoryCustom.findItem(userId,word,pageable);
|
Page<Item> itemList = itemRepositoryCustom.findItem(userId,word,pageable);
|
||||||
return PageableExecutionUtils.getPage(itemList.stream()
|
return PageableExecutionUtils.getPage(itemList.stream()
|
||||||
.map(ItemDto::createWithCategoryItemDto)
|
.map(ItemDto::createWithCategory)
|
||||||
.collect(Collectors.toList()),pageable,itemList::getTotalElements);
|
.collect(Collectors.toList()),pageable,itemList::getTotalElements);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,7 +94,6 @@ public class ItemServiceImpl implements ItemService {
|
|||||||
Long itemPrice,
|
Long itemPrice,
|
||||||
Long categoryId,
|
Long categoryId,
|
||||||
List<ItemOptionDto> itemOptionDtos) {
|
List<ItemOptionDto> itemOptionDtos) {
|
||||||
|
|
||||||
//find Store
|
//find Store
|
||||||
Store store = storeRepository.findByUserId(userId)
|
Store store = storeRepository.findByUserId(userId)
|
||||||
.orElseThrow(() -> new NotExistItemException("존재하지 않는 매장 입니다."));
|
.orElseThrow(() -> new NotExistItemException("존재하지 않는 매장 입니다."));
|
||||||
@@ -105,11 +102,13 @@ public class ItemServiceImpl implements ItemService {
|
|||||||
Category category = categoryRepository.findById(categoryId)
|
Category category = categoryRepository.findById(categoryId)
|
||||||
.orElseThrow(() -> new NotExistItemException("존재하지 않는 카테고리 입니다."));
|
.orElseThrow(() -> new NotExistItemException("존재하지 않는 카테고리 입니다."));
|
||||||
|
|
||||||
//create Item
|
List<ItemOption> itemOptions = itemOptionDtos
|
||||||
Item item = Item.createdFullItem(category,store,new ArrayList<>() ,itemName, itemPrice);
|
.stream()
|
||||||
|
.map(itemOptionDto -> ItemOption.of(itemOptionDto.getOptionType(), itemOptionDto.getName()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
//add ItemOption
|
Item createdItem = Item.of(itemName, itemPrice, category, store, itemOptions);
|
||||||
itemOptionDtos.forEach((itemOptionDto ->
|
|
||||||
itemOptionRepository.save(ItemOptionDto.createItemOption(itemOptionDto, item))));
|
itemRepository.save(createdItem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -128,10 +128,8 @@ public class ItemOwnerApiController {
|
|||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
public ItemOptionResponse(ItemOptionDto itemOptionDto) {
|
public ItemOptionResponse(ItemOptionDto itemOptionDto) {
|
||||||
|
|
||||||
this.id = itemOptionDto.getId();
|
this.id = itemOptionDto.getId();
|
||||||
this.optionType = itemOptionDto.getOptionType();
|
this.optionType = itemOptionDto.getOptionType();
|
||||||
this.price = itemOptionDto.getPrice();
|
|
||||||
this.name = itemOptionDto.getName();
|
this.name = itemOptionDto.getName();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -181,13 +179,11 @@ public class ItemOwnerApiController {
|
|||||||
private Long id;
|
private Long id;
|
||||||
private String name;
|
private String name;
|
||||||
private OptionType optionType;
|
private OptionType optionType;
|
||||||
private Long price;
|
|
||||||
|
|
||||||
public static ItemOptionDto createItemOptionDto(ItemOptionRequest itemOptionRequest){
|
public static ItemOptionDto createItemOptionDto(ItemOptionRequest itemOptionRequest){
|
||||||
return ItemOptionDto.builder()
|
return ItemOptionDto.builder()
|
||||||
.id(itemOptionRequest.getId())
|
.id(itemOptionRequest.getId())
|
||||||
.name(itemOptionRequest.getName())
|
.name(itemOptionRequest.getName())
|
||||||
.price(itemOptionRequest.getPrice())
|
|
||||||
.optionType(itemOptionRequest.getOptionType())
|
.optionType(itemOptionRequest.getOptionType())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
|||||||
@@ -20,20 +20,15 @@ public class ItemOptionDto {
|
|||||||
|
|
||||||
private OptionType optionType;
|
private OptionType optionType;
|
||||||
|
|
||||||
private Long price;
|
|
||||||
|
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
public ItemOptionDto (ItemOption itemOption){
|
public ItemOptionDto (ItemOption itemOption){
|
||||||
this.id = itemOption.getId();
|
this.id = itemOption.getId();
|
||||||
this.optionType = itemOption.getOptionType();
|
this.optionType = itemOption.getOptionType();
|
||||||
this.price = itemOption.getPrice();
|
|
||||||
this.name = itemOption.getName();
|
this.name = itemOption.getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ItemOption createItemOption (ItemOptionDto itemOptionDto, Item item){
|
public static ItemOption createItemOption (ItemOptionDto itemOptionDto, Item item){
|
||||||
|
return new ItemOption(itemOptionDto.getOptionType(), itemOptionDto.getName(),item);
|
||||||
return new ItemOption(itemOptionDto.getOptionType(),itemOptionDto.getPrice(),itemOptionDto.getName(),item);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,8 +22,6 @@ public class ItemOption extends BaseEntity {
|
|||||||
@Enumerated(EnumType.STRING)
|
@Enumerated(EnumType.STRING)
|
||||||
private OptionType optionType;
|
private OptionType optionType;
|
||||||
|
|
||||||
private Long price;
|
|
||||||
|
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
@ManyToOne(fetch = LAZY , cascade = CascadeType.ALL)
|
@ManyToOne(fetch = LAZY , cascade = CascadeType.ALL)
|
||||||
@@ -36,10 +34,16 @@ public class ItemOption extends BaseEntity {
|
|||||||
item.getItemOptions().add(this);
|
item.getItemOptions().add(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ItemOption(OptionType optionType, Long price, String name, Item item) {
|
public ItemOption(OptionType optionType, String name, Item item) {
|
||||||
this.optionType = optionType;
|
this.optionType = optionType;
|
||||||
this.price = price;
|
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.item = item;
|
this.item = item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ItemOption of(OptionType type, String name) {
|
||||||
|
ItemOption itemOption = new ItemOption();
|
||||||
|
itemOption.optionType = type;
|
||||||
|
itemOption.name = name;
|
||||||
|
return itemOption;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
package com.justpickup.storeservice.domain.itemoption.repository;
|
package com.justpickup.storeservice.domain.itemoption.repository;
|
||||||
|
|
||||||
import com.justpickup.storeservice.domain.item.entity.Item;
|
|
||||||
import com.justpickup.storeservice.domain.item.entity.QItem;
|
|
||||||
import com.justpickup.storeservice.domain.itemoption.entity.ItemOption;
|
import com.justpickup.storeservice.domain.itemoption.entity.ItemOption;
|
||||||
import com.justpickup.storeservice.domain.itemoption.entity.QItemOption;
|
import com.justpickup.storeservice.domain.itemoption.entity.QItemOption;
|
||||||
import com.querydsl.jpa.impl.JPAQueryFactory;
|
import com.querydsl.jpa.impl.JPAQueryFactory;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import static com.justpickup.storeservice.domain.item.entity.QItem.item;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class ItemOptionRepositoryCustom {
|
public class ItemOptionRepositoryCustom {
|
||||||
@@ -21,7 +20,9 @@ public class ItemOptionRepositoryCustom {
|
|||||||
|
|
||||||
return queryFactory.selectFrom(QItemOption.itemOption)
|
return queryFactory.selectFrom(QItemOption.itemOption)
|
||||||
.join(QItemOption.itemOption.item)
|
.join(QItemOption.itemOption.item)
|
||||||
.on(QItem.item.id.eq(itemId))
|
.on(item.id.eq(itemId))
|
||||||
.fetch();
|
.fetch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.justpickup.storeservice.domain.store.dto;
|
||||||
|
|
||||||
|
import com.justpickup.storeservice.domain.store.entity.Store;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Builder
|
||||||
|
public class StoreDto {
|
||||||
|
private Long id;
|
||||||
|
private String name;
|
||||||
|
private String phoneNumber;
|
||||||
|
|
||||||
|
public StoreDto(Store store) {
|
||||||
|
this.id = store.getId();
|
||||||
|
this.name = store.getName();
|
||||||
|
this.phoneNumber = store.getPhoneNumber();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -13,6 +13,7 @@ import lombok.NoArgsConstructor;
|
|||||||
|
|
||||||
import javax.persistence.*;
|
import javax.persistence.*;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static javax.persistence.FetchType.LAZY;
|
import static javax.persistence.FetchType.LAZY;
|
||||||
@@ -48,13 +49,14 @@ public class Store extends BaseEntity {
|
|||||||
private Long userId;
|
private Long userId;
|
||||||
|
|
||||||
@OneToMany(mappedBy = "store")
|
@OneToMany(mappedBy = "store")
|
||||||
private List<Category> categories;
|
private List<Category> categories = new ArrayList<>();
|
||||||
|
|
||||||
@OneToMany(mappedBy = "store")
|
@OneToMany(mappedBy = "store")
|
||||||
private List<Item> items;
|
private List<Item> items = new ArrayList<>();
|
||||||
|
|
||||||
@OneToMany(mappedBy = "store")
|
@OneToMany(mappedBy = "store")
|
||||||
private List<FavoriteStore> favoriteStores;
|
private List<FavoriteStore> favoriteStores = new ArrayList<>();
|
||||||
|
|
||||||
// == 연관관계 편의 메소드 == //
|
// == 연관관계 편의 메소드 == //
|
||||||
public void addCategory(Category category) {
|
public void addCategory(Category category) {
|
||||||
categories.add(category);
|
categories.add(category);
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package com.justpickup.storeservice.domain.store.exception;
|
||||||
|
|
||||||
|
import com.justpickup.storeservice.global.exception.CustomException;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
|
||||||
|
public class NotExistStoreException extends CustomException {
|
||||||
|
|
||||||
|
public NotExistStoreException(String message) {
|
||||||
|
super(HttpStatus.CONFLICT, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
package com.justpickup.storeservice.domain.store.repository;
|
package com.justpickup.storeservice.domain.store.repository;
|
||||||
|
|
||||||
import com.justpickup.storeservice.domain.favoritestore.entity.FavoriteStore;
|
|
||||||
import com.justpickup.storeservice.domain.favoritestore.entity.QFavoriteStore;
|
import com.justpickup.storeservice.domain.favoritestore.entity.QFavoriteStore;
|
||||||
import com.justpickup.storeservice.domain.store.dto.SearchStoreCondition;
|
import com.justpickup.storeservice.domain.store.dto.SearchStoreCondition;
|
||||||
import com.justpickup.storeservice.domain.store.dto.SearchStoreResult;
|
import com.justpickup.storeservice.domain.store.dto.SearchStoreResult;
|
||||||
@@ -83,7 +82,7 @@ public class StoreRepositoryCustom {
|
|||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
private NumberExpression<Double> getHaversineDistance (double SearchLatitude, double SearchLongitude){
|
private NumberExpression<Double> getHaversineDistance(double SearchLatitude, double SearchLongitude){
|
||||||
Expression<Double> latitude = Expressions.constant(SearchLatitude);
|
Expression<Double> latitude = Expressions.constant(SearchLatitude);
|
||||||
Expression<Double> longitude = Expressions.constant(SearchLongitude);
|
Expression<Double> longitude = Expressions.constant(SearchLongitude);
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.justpickup.storeservice.domain.store.service;
|
|||||||
|
|
||||||
import com.justpickup.storeservice.domain.store.dto.SearchStoreCondition;
|
import com.justpickup.storeservice.domain.store.dto.SearchStoreCondition;
|
||||||
import com.justpickup.storeservice.domain.store.dto.SearchStoreResult;
|
import com.justpickup.storeservice.domain.store.dto.SearchStoreResult;
|
||||||
|
import com.justpickup.storeservice.domain.store.dto.StoreDto;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
import org.springframework.data.domain.SliceImpl;
|
import org.springframework.data.domain.SliceImpl;
|
||||||
|
|
||||||
@@ -10,4 +11,5 @@ import java.util.List;
|
|||||||
public interface StoreService {
|
public interface StoreService {
|
||||||
SliceImpl<SearchStoreResult> findSearchStoreScroll(SearchStoreCondition condition, Pageable pageable);
|
SliceImpl<SearchStoreResult> findSearchStoreScroll(SearchStoreCondition condition, Pageable pageable);
|
||||||
List<SearchStoreResult> findFavoriteStore(SearchStoreCondition condition, Long userId);
|
List<SearchStoreResult> findFavoriteStore(SearchStoreCondition condition, Long userId);
|
||||||
|
StoreDto findStore(Long storeId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,10 @@ package com.justpickup.storeservice.domain.store.service;
|
|||||||
import com.justpickup.storeservice.domain.favoritestore.repository.FavoriteStoreCustom;
|
import com.justpickup.storeservice.domain.favoritestore.repository.FavoriteStoreCustom;
|
||||||
import com.justpickup.storeservice.domain.store.dto.SearchStoreCondition;
|
import com.justpickup.storeservice.domain.store.dto.SearchStoreCondition;
|
||||||
import com.justpickup.storeservice.domain.store.dto.SearchStoreResult;
|
import com.justpickup.storeservice.domain.store.dto.SearchStoreResult;
|
||||||
|
import com.justpickup.storeservice.domain.store.dto.StoreDto;
|
||||||
|
import com.justpickup.storeservice.domain.store.entity.Store;
|
||||||
|
import com.justpickup.storeservice.domain.store.exception.NotExistStoreException;
|
||||||
|
import com.justpickup.storeservice.domain.store.repository.StoreRepository;
|
||||||
import com.justpickup.storeservice.domain.store.repository.StoreRepositoryCustom;
|
import com.justpickup.storeservice.domain.store.repository.StoreRepositoryCustom;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
@@ -10,7 +14,6 @@ import org.springframework.data.domain.SliceImpl;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@@ -18,6 +21,7 @@ public class StoreServiceImpl implements StoreService {
|
|||||||
|
|
||||||
private final StoreRepositoryCustom storeRepositoryCustom;
|
private final StoreRepositoryCustom storeRepositoryCustom;
|
||||||
private final FavoriteStoreCustom favoriteStoreCustom;
|
private final FavoriteStoreCustom favoriteStoreCustom;
|
||||||
|
private final StoreRepository storeRepository;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SliceImpl<SearchStoreResult> findSearchStoreScroll(SearchStoreCondition condition, Pageable pageable) {
|
public SliceImpl<SearchStoreResult> findSearchStoreScroll(SearchStoreCondition condition, Pageable pageable) {
|
||||||
@@ -41,4 +45,12 @@ public class StoreServiceImpl implements StoreService {
|
|||||||
});
|
});
|
||||||
return favoriteStores;
|
return favoriteStores;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StoreDto findStore(Long storeId) {
|
||||||
|
Store store = storeRepository.findById(storeId)
|
||||||
|
.orElseThrow(() -> new NotExistStoreException(storeId + "는 없는 매장 고유번호입니다."));
|
||||||
|
|
||||||
|
return new StoreDto(store);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,14 @@
|
|||||||
package com.justpickup.storeservice.domain.store.web;
|
package com.justpickup.storeservice.domain.store.web;
|
||||||
|
|
||||||
|
import com.justpickup.storeservice.domain.store.dto.StoreDto;
|
||||||
|
import com.justpickup.storeservice.domain.store.service.StoreService;
|
||||||
|
import com.justpickup.storeservice.global.dto.Result;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
@@ -9,4 +17,26 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
@RequestMapping("/store")
|
@RequestMapping("/store")
|
||||||
public class StoreController {
|
public class StoreController {
|
||||||
|
|
||||||
|
private final StoreService storeService;
|
||||||
|
|
||||||
|
@GetMapping("/{storeId}")
|
||||||
|
public ResponseEntity<Result> getStore(@PathVariable("storeId") Long storeId) {
|
||||||
|
StoreDto storeDto = storeService.findStore(storeId);
|
||||||
|
|
||||||
|
GetStoreResponse getStoreResponse = new GetStoreResponse(storeDto);
|
||||||
|
return ResponseEntity.ok(Result.createSuccessResult(getStoreResponse));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data @NoArgsConstructor
|
||||||
|
static class GetStoreResponse {
|
||||||
|
private Long id;
|
||||||
|
private String name;
|
||||||
|
private String phoneNumber;
|
||||||
|
|
||||||
|
public GetStoreResponse(StoreDto storeDto) {
|
||||||
|
this.id = storeDto.getId();
|
||||||
|
this.name = storeDto.getName();
|
||||||
|
this.phoneNumber = storeDto.getPhoneNumber();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,108 @@
|
|||||||
|
package com.justpickup.storeservice.global;
|
||||||
|
|
||||||
|
import com.justpickup.storeservice.domain.category.entity.Category;
|
||||||
|
import com.justpickup.storeservice.domain.category.repository.CategoryRepository;
|
||||||
|
import com.justpickup.storeservice.domain.favoritestore.entity.FavoriteStore;
|
||||||
|
import com.justpickup.storeservice.domain.favoritestore.repository.FavoriteStoreRepository;
|
||||||
|
import com.justpickup.storeservice.domain.item.entity.Item;
|
||||||
|
import com.justpickup.storeservice.domain.item.repository.ItemRepository;
|
||||||
|
import com.justpickup.storeservice.domain.itemoption.entity.ItemOption;
|
||||||
|
import com.justpickup.storeservice.domain.itemoption.entity.OptionType;
|
||||||
|
import com.justpickup.storeservice.domain.map.entity.Map;
|
||||||
|
import com.justpickup.storeservice.domain.store.entity.Store;
|
||||||
|
import com.justpickup.storeservice.domain.store.repository.StoreRepository;
|
||||||
|
import com.justpickup.storeservice.global.entity.Address;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.boot.CommandLineRunner;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class SqlCommandLineRunner implements CommandLineRunner {
|
||||||
|
|
||||||
|
private final StoreRepository storeRepository;
|
||||||
|
private final FavoriteStoreRepository favoriteStoreRepository;
|
||||||
|
private final ItemRepository itemRepository;
|
||||||
|
private final CategoryRepository categoryRepository;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(String... args) throws Exception {
|
||||||
|
List<Store> stores = new ArrayList<>();
|
||||||
|
|
||||||
|
createStores(storeRepository, stores);
|
||||||
|
|
||||||
|
createFavoriteStore(favoriteStoreRepository, stores);
|
||||||
|
|
||||||
|
createItemAndCategories(itemRepository, categoryRepository, stores);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createItemAndCategories(ItemRepository itemRepository, CategoryRepository categoryRepository, List<Store> stores) {
|
||||||
|
stores.forEach(store -> {
|
||||||
|
Category 카페인 = categoryRepository.save(Category.of("카페인", 0, store));
|
||||||
|
Category 디카페인 = categoryRepository.save(Category.of("디카페인", 1, store));
|
||||||
|
Category 티 = categoryRepository.save(Category.of("티", 2, store));
|
||||||
|
|
||||||
|
ItemOption ice = ItemOption.of(OptionType.REQUIRED, "ICE");
|
||||||
|
ItemOption hot = ItemOption.of(OptionType.REQUIRED, "HOT");
|
||||||
|
|
||||||
|
Item 아메리카노 = Item.of("아메리카노", 1500L, 카페인, store, List.of(ice, hot));
|
||||||
|
Item 카페라떼 = Item.of("카페라떼", 2000L, 카페인, store, List.of(ice, hot));
|
||||||
|
Item 카페모카 = Item.of("카페모카", 3900L, 카페인, store, List.of(ice, hot));
|
||||||
|
Item 콜드브루 = Item.of("콜드브루", 2500L, 카페인, store, List.of(ice));
|
||||||
|
Item 녹차라떼 = Item.of("녹차라떼", 3000L, 디카페인, store, List.of(ice, hot));
|
||||||
|
Item 딸기라떼 = Item.of("딸기라떼", 3000L, 디카페인, store, List.of(ice, hot));
|
||||||
|
Item 녹차 = Item.of("녹차", 3000L, 티, store, List.of(hot));
|
||||||
|
Item 히비스커스 = Item.of("히비스커스 티", 3000L, 티, store, List.of(hot));
|
||||||
|
itemRepository.saveAll(List.of(아메리카노, 카페라떼, 콜드브루, 녹차라떼, 딸기라떼, 녹차, 히비스커스));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void createFavoriteStore(FavoriteStoreRepository favoriteStoreRepository, List<Store> stores) {
|
||||||
|
List<Long> userList = List.of(1L,2L,3L,4L,5L,6L,7L);
|
||||||
|
userList.forEach(userId -> stores.forEach(store -> favoriteStoreRepository.save(FavoriteStore.of(userId, store))));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createStores(StoreRepository storeRepository, List<Store> stores) {
|
||||||
|
stores.add(
|
||||||
|
Store.of(
|
||||||
|
new Address("서울시", "마포구 도화동", "201-20"),
|
||||||
|
Map.of(37.5398271003404, 126.94769672415691),
|
||||||
|
1L,
|
||||||
|
"커피온리 마포역점"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
stores.add(
|
||||||
|
Store.of(
|
||||||
|
new Address("서울시", "마포구 도화동", "50-10"),
|
||||||
|
Map.of(37.54010719003089, 126.94556661330861),
|
||||||
|
2L,
|
||||||
|
"만랩커피 마포점"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
stores.add(
|
||||||
|
Store.of(
|
||||||
|
new Address("서울시", "마포구 도화동", "555"),
|
||||||
|
Map.of(37.539797393793755, 126.9453578838543),
|
||||||
|
3L,
|
||||||
|
"이디야커피 마포오벨리스크점"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
stores.add(
|
||||||
|
Store.of(
|
||||||
|
new Address("서울시", "영등포구 도림로", "31길 2"),
|
||||||
|
Map.of(37.493033141569505, 126.89593667847592),
|
||||||
|
4L,
|
||||||
|
"이디야커피 대림역점"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
storeRepository.saveAll(stores);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -45,17 +45,9 @@ class CategoryOwnerApiControllerTest {
|
|||||||
@MockBean
|
@MockBean
|
||||||
private CategoryService categoryService;
|
private CategoryService categoryService;
|
||||||
|
|
||||||
@MockBean
|
|
||||||
private StoreRepository storeRepository;
|
|
||||||
|
|
||||||
@MockBean
|
|
||||||
private FavoriteStoreRepository favoriteStoreRepository;
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("카테고리리스트_가져오기_성공")
|
@DisplayName("카테고리리스트_가져오기_성공")
|
||||||
void getCategoryList_success() throws Exception {
|
void getCategoryList_success() throws Exception {
|
||||||
|
|
||||||
//given
|
//given
|
||||||
Long storeId = 1L;
|
Long storeId = 1L;
|
||||||
List<CategoryDto> categoryDtoList = new ArrayList<>();
|
List<CategoryDto> categoryDtoList = new ArrayList<>();
|
||||||
@@ -75,7 +67,7 @@ class CategoryOwnerApiControllerTest {
|
|||||||
.order(2)
|
.order(2)
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
given(categoryService.getCategoryList(any())).willReturn(categoryDtoList);
|
given(categoryService.getCategoriesWithItem(any())).willReturn(categoryDtoList);
|
||||||
//when
|
//when
|
||||||
|
|
||||||
ResultActions actions = mockMvc.perform(MockMvcRequestBuilders.get("/api/owner/category")
|
ResultActions actions = mockMvc.perform(MockMvcRequestBuilders.get("/api/owner/category")
|
||||||
@@ -108,7 +100,6 @@ class CategoryOwnerApiControllerTest {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("카테고리리스트_수정_성공")
|
@DisplayName("카테고리리스트_수정_성공")
|
||||||
void putCategoryList_success() throws Exception {
|
void putCategoryList_success() throws Exception {
|
||||||
|
|||||||
@@ -56,12 +56,6 @@ class ItemOwnerApiControllerTest {
|
|||||||
@MockBean
|
@MockBean
|
||||||
ItemService itemService;
|
ItemService itemService;
|
||||||
|
|
||||||
@MockBean
|
|
||||||
private StoreRepository storeRepository;
|
|
||||||
|
|
||||||
@MockBean
|
|
||||||
private FavoriteStoreRepository favoriteStoreRepository;
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("상품 리스트 조회")
|
@DisplayName("상품 리스트 조회")
|
||||||
void getItemList() throws Exception {
|
void getItemList() throws Exception {
|
||||||
@@ -177,9 +171,9 @@ class ItemOwnerApiControllerTest {
|
|||||||
Long categoryId = 1L;
|
Long categoryId = 1L;
|
||||||
|
|
||||||
List<ItemOwnerApiController.ItemRequest.ItemOptionRequest> requiredOption =
|
List<ItemOwnerApiController.ItemRequest.ItemOptionRequest> requiredOption =
|
||||||
List.of(new ItemOwnerApiController.ItemRequest.ItemOptionRequest(null, "HOT",OptionType.REQUIRED,null));
|
List.of(new ItemOwnerApiController.ItemRequest.ItemOptionRequest(null, "HOT",OptionType.REQUIRED));
|
||||||
List<ItemOwnerApiController.ItemRequest.ItemOptionRequest> otherOption =
|
List<ItemOwnerApiController.ItemRequest.ItemOptionRequest> otherOption =
|
||||||
List.of(new ItemOwnerApiController.ItemRequest.ItemOptionRequest(null, "샷 추가",OptionType.OTHER,null));
|
List.of(new ItemOwnerApiController.ItemRequest.ItemOptionRequest(null, "샷 추가", OptionType.OTHER));
|
||||||
|
|
||||||
|
|
||||||
ItemOwnerApiController.ItemRequest itemRequest =
|
ItemOwnerApiController.ItemRequest itemRequest =
|
||||||
@@ -208,12 +202,10 @@ class ItemOwnerApiControllerTest {
|
|||||||
fieldWithPath("requiredOption[*].id").description("옵션 고유번호"),
|
fieldWithPath("requiredOption[*].id").description("옵션 고유번호"),
|
||||||
fieldWithPath("requiredOption[*].name").description("옵션 이름"),
|
fieldWithPath("requiredOption[*].name").description("옵션 이름"),
|
||||||
fieldWithPath("requiredOption[*].optionType").description("옵션 타입"),
|
fieldWithPath("requiredOption[*].optionType").description("옵션 타입"),
|
||||||
fieldWithPath("requiredOption[*].price").description("옵션 가격"),
|
|
||||||
fieldWithPath("otherOption").description("추가옵션"),
|
fieldWithPath("otherOption").description("추가옵션"),
|
||||||
fieldWithPath("otherOption[*].id").description("옵션 고유번호"),
|
fieldWithPath("otherOption[*].id").description("옵션 고유번호"),
|
||||||
fieldWithPath("otherOption[*].name").description("옵션 이름"),
|
fieldWithPath("otherOption[*].name").description("옵션 이름"),
|
||||||
fieldWithPath("otherOption[*].optionType").description("옵션 타입"),
|
fieldWithPath("otherOption[*].optionType").description("옵션 타입")
|
||||||
fieldWithPath("otherOption[*].price").description("옵션 가격")
|
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -227,9 +219,9 @@ class ItemOwnerApiControllerTest {
|
|||||||
Long categoryId = 1L;
|
Long categoryId = 1L;
|
||||||
|
|
||||||
List<ItemOwnerApiController.ItemRequest.ItemOptionRequest> requiredOption =
|
List<ItemOwnerApiController.ItemRequest.ItemOptionRequest> requiredOption =
|
||||||
List.of(new ItemOwnerApiController.ItemRequest.ItemOptionRequest(null, "HOT",OptionType.REQUIRED,null));
|
List.of(new ItemOwnerApiController.ItemRequest.ItemOptionRequest(null, "HOT",OptionType.REQUIRED));
|
||||||
List<ItemOwnerApiController.ItemRequest.ItemOptionRequest> otherOption =
|
List<ItemOwnerApiController.ItemRequest.ItemOptionRequest> otherOption =
|
||||||
List.of(new ItemOwnerApiController.ItemRequest.ItemOptionRequest(null, "샷 추가",OptionType.OTHER,null));
|
List.of(new ItemOwnerApiController.ItemRequest.ItemOptionRequest(null, "샷 추가",OptionType.OTHER));
|
||||||
|
|
||||||
|
|
||||||
ItemOwnerApiController.ItemRequest itemRequest =
|
ItemOwnerApiController.ItemRequest itemRequest =
|
||||||
@@ -257,12 +249,10 @@ class ItemOwnerApiControllerTest {
|
|||||||
fieldWithPath("requiredOption[*].id").description("옵션 고유번호"),
|
fieldWithPath("requiredOption[*].id").description("옵션 고유번호"),
|
||||||
fieldWithPath("requiredOption[*].name").description("옵션 이름"),
|
fieldWithPath("requiredOption[*].name").description("옵션 이름"),
|
||||||
fieldWithPath("requiredOption[*].optionType").description("옵션 타입"),
|
fieldWithPath("requiredOption[*].optionType").description("옵션 타입"),
|
||||||
fieldWithPath("requiredOption[*].price").description("옵션 가격"),
|
|
||||||
fieldWithPath("otherOption").description("추가옵션"),
|
fieldWithPath("otherOption").description("추가옵션"),
|
||||||
fieldWithPath("otherOption[*].id").description("옵션 고유번호"),
|
fieldWithPath("otherOption[*].id").description("옵션 고유번호"),
|
||||||
fieldWithPath("otherOption[*].name").description("옵션 이름"),
|
fieldWithPath("otherOption[*].name").description("옵션 이름"),
|
||||||
fieldWithPath("otherOption[*].optionType").description("옵션 타입"),
|
fieldWithPath("otherOption[*].optionType").description("옵션 타입")
|
||||||
fieldWithPath("otherOption[*].price").description("옵션 가격")
|
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package com.justpickup.storeservice.domain.store.web;
|
||||||
|
|
||||||
|
import com.justpickup.storeservice.config.TestConfig;
|
||||||
|
import com.justpickup.storeservice.domain.store.dto.StoreDto;
|
||||||
|
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.test.web.servlet.MockMvc;
|
||||||
|
import org.springframework.test.web.servlet.ResultActions;
|
||||||
|
|
||||||
|
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.pathParameters;
|
||||||
|
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 = "just-pickup.com", uriPort = 8000)
|
||||||
|
class StoreControllerTest {
|
||||||
|
|
||||||
|
private final String url = "/store";
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
MockMvc mockMvc;
|
||||||
|
|
||||||
|
@MockBean
|
||||||
|
StoreService storeService;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("[store] 매장 정보 가져오기")
|
||||||
|
void getStore() throws Exception {
|
||||||
|
//given
|
||||||
|
String storeId = "1";
|
||||||
|
given(storeService.findStore(1L)).willReturn(getWillReturnStore());
|
||||||
|
|
||||||
|
//when
|
||||||
|
ResultActions actions = mockMvc.perform(get("/store/{storeId}", storeId));
|
||||||
|
|
||||||
|
//then
|
||||||
|
actions.andExpect(status().isOk())
|
||||||
|
.andDo(print())
|
||||||
|
.andDo(document("store-get",
|
||||||
|
pathParameters(
|
||||||
|
parameterWithName("storeId").description("매장 고유번호")
|
||||||
|
),
|
||||||
|
responseFields(
|
||||||
|
fieldWithPath("code").description("결과 코드 SUCCESS/ERROR"),
|
||||||
|
fieldWithPath("message").description("메시지"),
|
||||||
|
fieldWithPath("data.id").description("매장 고유번호"),
|
||||||
|
fieldWithPath("data.name").description("매장 이름"),
|
||||||
|
fieldWithPath("data.phoneNumber").description("매장 번호")
|
||||||
|
)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
private StoreDto getWillReturnStore() {
|
||||||
|
return StoreDto.builder().id(1L).name("이디야커피 대림역점").phoneNumber("010-1234-5678").build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -93,16 +93,17 @@ class StoreCustomerApiControllerTest {
|
|||||||
fieldWithPath("message").description("메시지"),
|
fieldWithPath("message").description("메시지"),
|
||||||
fieldWithPath("data[*].id").description("매장 고유번호"),
|
fieldWithPath("data[*].id").description("매장 고유번호"),
|
||||||
fieldWithPath("data[*].name").description("매장 이름"),
|
fieldWithPath("data[*].name").description("매장 이름"),
|
||||||
fieldWithPath("data[*].distance").description("고객과의 거리차이 m/km")
|
fieldWithPath("data[*].distance").description("고객과의 거리차이 m/km"),
|
||||||
|
fieldWithPath("data[*].favoriteCounts").description("즐겨찾기 회수")
|
||||||
)
|
)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private List<SearchStoreResult> getWillReturnSearchStore(){
|
private List<SearchStoreResult> getWillReturnSearchStore(){
|
||||||
SearchStoreResult result_1 = new SearchStoreResult(1L, "이디야커피 마포오벨리스크점", 145.11980562222007);
|
SearchStoreResult result_1 = new SearchStoreResult(1L, "이디야커피 마포오벨리스크점", 145.11980562222007, 5L);
|
||||||
SearchStoreResult result_2 = new SearchStoreResult(2L, "만랩커피 마포점", 150.97181089895466);
|
SearchStoreResult result_2 = new SearchStoreResult(2L, "만랩커피 마포점", 150.97181089895466, 5L);
|
||||||
SearchStoreResult result_3 = new SearchStoreResult(3L, "커피온리 마포역점", 341.25696860337655);
|
SearchStoreResult result_3 = new SearchStoreResult(3L, "커피온리 마포역점", 341.25696860337655, 5L);
|
||||||
|
|
||||||
return List.of(result_1,result_2,result_3);
|
return List.of(result_1,result_2,result_3);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user