feat(store-service, owner-vue): 아이템 수정하기 개발

아이템 수정
아이템 옵션 추가 기능 개발
This commit is contained in:
hoon7566
2022-02-25 16:38:24 +09:00
parent 60de553804
commit 54f19fcad2
16 changed files with 485 additions and 41 deletions

View File

@@ -0,0 +1,54 @@
package com.justpickup.ownerapigatewayservice.filter;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
@Component
@Slf4j
public class GlobalFilter extends AbstractGatewayFilterFactory<GlobalFilter.Config> {
private static final String TEST_CIRCUIT_BREAKER = "testCircuitBreaker";
public GlobalFilter(){
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest(); // reactive포함된거로 import
ServerHttpResponse response = exchange.getResponse();
log.info("Global com.example.scg.filter baseMessgae: {}", config.getBaseMessage());
// Global pre Filter
if (config.isPreLogger()){
log.info("Global Filter Start: request id -> {}" , request.getId());
log.info("Global Filter Start: request path -> {}" , request.getPath());
}
// Global Post Filter
//Mono는 webflux에서 단일값 전송할때 Mono값으로 전송
return chain.filter(exchange).then(Mono.fromRunnable(()->{
if (config.isPostLogger()){
log.info("Global Filter End: response statuscode -> {}" , response.getStatusCode());
}
}));
};
}
@Data
public static class Config {
private String baseMessage;
private boolean preLogger;
private boolean postLogger;
}
}

View File

@@ -14,16 +14,24 @@ spring:
cloud:
gateway:
default-filters:
- name: GlobalFilter
args:
baseMessage: Spring Cloud Gateway Global Filter
preLogger: true
postLogger: true
globalcors:
cors-configurations:
'[/**]':
allowedOrigins: "http://localhost:8080"
allowedOrigins: "*"
allowedHeaders: "*"
allowedMethods:
- POST
- GET
- PUT
- OPTIONS
- DELETE
# add-to-simple-url-handler-mapping: true
routes:
- id: owner-frontend-service
uri: lb://OWNER-FRONTEND-SERVICE

1
owner-vue/.env Normal file
View File

@@ -0,0 +1 @@
VUE_APP_OWNER_SERVICE_BASEURL=http://localhost:8001

View File

@@ -3,9 +3,13 @@ import App from './App.vue'
import vuetify from './plugins/vuetify'
import router from './router'
import axios from "axios";
import customUtil from './util/customUtil'
Vue.config.productionTip = false
Vue.prototype.$axios = axios;
Vue.prototype.$customUtil = customUtil;
console.log(process.env)
new Vue({
vuetify,

View File

@@ -0,0 +1,9 @@
export default {
deepCopy : function (o) {
let result = {};
if (typeof o === "object" && o !== null)
for (let i in o) result[i] = this.deepCopy(o[i]);
else result = o;
return result;
}
}

View File

@@ -114,9 +114,11 @@ export default {
}
data.categoryList.push(category)
})
console.log(data)
this.$axios({
method:'put',
url:'/store-service/category',
url:process.env.VUE_APP_OWNER_SERVICE_BASEURL+'/store-service/category',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json;charset=UTF-8'
@@ -133,9 +135,10 @@ export default {
},
getCategoryList:function(){
var vm =this;
console.log(process.env.OWNER_SERVICE_BASEURL)
this.$axios({
method:'get',
url:'/store-service/category',
url: process.env.VUE_APP_OWNER_SERVICE_BASEURL+'/store-service/category',
responseType:'json'
})
.then(function (response) {
@@ -145,6 +148,7 @@ export default {
}
},
mounted() {
alert()
this.getCategoryList();
}
}

View File

@@ -0,0 +1,77 @@
<template>
<v-dialog
v-model="dialog"
width="500"
>
<template v-slot:activator="{ on, attrs }">
<v-btn
elevation="2"
icon
v-bind="attrs"
v-on="on"
><v-icon>mdi-pencil</v-icon></v-btn>
</template>
<v-card>
<v-card-title class="text-h5 grey lighten-2">
추가하기
</v-card-title>
<v-card-text>
<v-text-field
label="옵션명*"
v-model="data"
@keyup.enter="addItemOption"
required
/>
</v-card-text>
<v-divider></v-divider>
<v-card-actions >
<v-spacer></v-spacer>
<v-btn
color="primary"
text
@click="addItemOption"
>
Save
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script>
export default {
name: "MenuItem",
data(){
return {
dialog : false,
data: "",
}
},
props:{
optionType:String
},
watch:{
dialog(){
this.data = '';
if(this.dialog) this.$emit("init");
}
},
methods:{
addItemOption : function () {
if(!this.data) return;
console.log(this.optionType)
this.dialog = false
this.$emit('addItemOption',this.data,this.optionType)
}
}
}
</script>
<style scoped>
</style>

View File

@@ -8,6 +8,7 @@
:color="modalSet.color.red"
:name="modalSet.words.register"
@save="itemSave"
@addItemOption="addItemOption"
/>
</v-subheader>
@@ -43,8 +44,9 @@
:icon="modalSet.icon.modify"
:color="modalSet.color.primary"
:name="modalSet.words.modify"
@init="editItem(item)"
@init="editModalOpen(item)"
@save="itemSave"
@addItemOption="addItemOption"
/>
</template>
@@ -111,10 +113,10 @@ export default {
itemId : Number,
itemName : String,
itemPrice : Number,
category: String,
categoryId: Number,
categoryList : ['카테고리1','카테고리2'],
requiredOption : ['Ice' , 'Hot'],
otherOption : ['얼음많이','샷추가','생크림많이','덜 뜨겁게']
requiredOption : [],
otherOption : []
},
page: 1,
pageCount: 1,
@@ -145,7 +147,7 @@ export default {
}
this.$axios({
method:'get',
url:'/store-service/item',
url:process.env.VUE_APP_OWNER_SERVICE_BASEURL+'/store-service/item',
params : searchParam,
responseType:'json'
})
@@ -161,7 +163,7 @@ export default {
this.modalData = {
itemName : '',
itemPrice : 0,
category: '',
categoryId: 0,
categoryList : [],
requiredOption : [],
otherOption : []
@@ -169,41 +171,73 @@ export default {
this.$axios({
method:'get',
url:'/store-service/category',
url: process.env.VUE_APP_OWNER_SERVICE_BASEURL+'/store-service/category',
responseType:'json'
})
.then(function (response) {
response.data.data.forEach(function (ele){
vm.modalData.categoryList.push(ele.name)
vm.modalData.categoryList.push(ele)
})
});
},
editItem:function(item){
editModalOpen:function(item){
var vm = this
this.getModalData();
// var vm =this;
this.$axios({
method:'get',
url:'/store-service/item/'+item.id,
url: process.env.VUE_APP_OWNER_SERVICE_BASEURL+'/store-service/item/'+item.id,
responseType:'json'
})
.then(function (response) {
console.log(response)
var item = response.data.data;
console.log(item)
vm.modalData.itemId = item.id;
vm.modalData.itemName = item.name;
vm.modalData.itemPrice = item.price;
vm.modalData.category = item.categoryName;
vm.modalData.categoryId = item.categoryId;
item.itemOptions.forEach(function(ele){
console.log(ele)
if(ele.optionType === "REQUIRED")
vm.modalData.requiredOption.push(ele.name)
vm.modalData.requiredOption.push(ele)
else
vm.modalData.otherOption.push(ele.name)
vm.modalData.otherOption.push(ele)
})
});
console.log(this.modalData)
},
itemSave:function(){
console.log(this.modalData)
var method =''
var itemData = this.modalData;
if (this.modalData.itemId!=null)
method='put'
else
method='post'
this.$axios({
method:method,
url: process.env.VUE_APP_OWNER_SERVICE_BASEURL+'/store-service/item',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json;charset=UTF-8'
},
data: itemData,
responseType:'json'
})
.then(response => console.log(response))
.catch(reason => console.log(reason))
},
addItemOption:function (itemOptionValue,type){
var item = {
name:itemOptionValue,
optionType:type
}
if(type ==='REQUIRED')
this.modalData.requiredOption.push(item)
else
this.modalData.otherOption.push(item)
}
},

View File

@@ -0,0 +1,177 @@
<template>
<v-dialog
v-model="dialog"
width="500"
>
<template v-slot:activator="{ on, attrs }">
<v-btn
elevation="2"
icon
:color="color"
v-bind="attrs"
v-on="on"
><v-icon>{{ icon }}</v-icon></v-btn>
</template>
<v-card>
<v-card-title>
<span class="text-h5">{{ name }}</span>
</v-card-title>
<v-card-text>
<v-container>
<v-row>
<v-col
cols="12"
sm="12"
md="12"
>
<v-text-field
v-model="modalData.itemName"
label="이름*"
required
/>
</v-col>
<v-col
cols="12"
sm="12"
md="12"
>
<v-text-field
v-model="modalData.itemPrice"
label="가격*"
hint="example of helper text only on focus"
/>
</v-col>
<v-col
cols="12"
sm="12"
>
<v-select
v-model="modalData.categoryId"
:items="modalData.categoryList"
item-text="name"
item-value="categoryId"
label="카테고리*"
required
/>
</v-col>
<v-col
cols="12"
sm="10"
>
<v-select
v-model="modalData.requiredOption"
:items="modalData.requiredOption"
item-text="name"
item-value="id"
label="필수 옵션*"
multiple
/>
</v-col>
<v-col
cols="12"
sm="2"
>
<item-option
@addItemOption="addItemOption"
:optionType="req"
/>
</v-col>
<v-col
cols="12"
sm="10"
>
<v-select
v-model="modalData.otherOption"
:items="modalData.otherOption"
item-text="name"
item-value="id"
label="기타 옵션"
multiple
/>
</v-col>
<v-col
cols="12"
sm="2"
>
<item-option
@addItemOption="addItemOption"
:optionType="otr"
/>
</v-col>
</v-row>
</v-container>
<small>*indicates required field</small>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn
color="blue darken-1"
text
@click="dialog = false"
>
Close
</v-btn>
<v-btn
color="blue darken-1"
text
@click="save"
>
Save
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script>
import ItemOption from "@/views/ItemOption";
export default {
name: "MenuItem",
components:{
ItemOption
},
data(){
return {
dialog : false,
req: 'REQUIRED',
otr: 'OTHER',
}
},
watch:{
dialog(){
if(this.dialog) this.$emit("init");
},
},
props:{
modalData: {
itemName : String,
itemPrice : Number,
category: String,
categoryList : Array,
requiredOption : Array,
otherOption : Array,
},
icon : String,
color : String,
name : String,
},
methods:{
save : function () {
console.log('save!')
this.dialog = false
this.$emit('save')
},
addItemOption : function (itemOptionValue,optionType){
console.log("saveOption",itemOptionValue,optionType)
this.$emit("addItemOption",itemOptionValue,optionType)
}
}
}
</script>
<style scoped>
</style>

View File

@@ -2,16 +2,4 @@ module.exports = {
transpileDependencies: [
'vuetify'
],
devServer:{
proxy:{
'store-service/' :{
target: 'http://localhost:8001',
ws:true,
},
'order-service/' :{
target: 'http://localhost:8001',
ws:true,
}
}
}
}

View File

@@ -62,6 +62,11 @@ public class Item extends BaseEntity {
category.getItems().add(this);
}
public void setItemNameAndPrice(String name , Long price){
this.name = name;
this.price = price;
}
// == 생성 메소드 == //
public static Item createdItem(Category category, Store store, List<ItemOption> itemOptions) {
Item item = new Item();

View File

@@ -1,6 +1,8 @@
package com.justpickup.storeservice.domain.item.service;
import com.justpickup.storeservice.domain.item.dto.ItemDto;
import com.justpickup.storeservice.domain.item.web.ItemController;
import com.justpickup.storeservice.domain.itemoption.dto.ItemOptionDto;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
@@ -11,4 +13,6 @@ public interface ItemService {
ItemDto findItemByItemId(Long itemId);
Page<ItemDto> findItemList(Long storeId,String word, Pageable pageable);
void putItem(Long itemId, String itemName, Long itemPrice, List<ItemOptionDto> itemOption);
}

View File

@@ -5,6 +5,9 @@ import com.justpickup.storeservice.domain.item.entity.Item;
import com.justpickup.storeservice.domain.item.exception.NotExistItemException;
import com.justpickup.storeservice.domain.item.repository.ItemRepository;
import com.justpickup.storeservice.domain.item.repository.ItemRepositoryCustom;
import com.justpickup.storeservice.domain.itemoption.dto.ItemOptionDto;
import com.justpickup.storeservice.domain.itemoption.entity.ItemOption;
import com.justpickup.storeservice.domain.itemoption.repository.ItemOptionRepository;
import com.justpickup.storeservice.domain.store.repository.StoreRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -14,6 +17,8 @@ import org.springframework.data.support.PageableExecutionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
@Service
@@ -23,6 +28,7 @@ import java.util.stream.Collectors;
public class ItemServiceImpl implements ItemService {
private final ItemRepository itemRepository;
private final ItemOptionRepository itemOptionRepository;
private final ItemRepositoryCustom itemRepositoryCustom;
private final StoreRepository storeRepository;
@@ -44,5 +50,22 @@ public class ItemServiceImpl implements ItemService {
.collect(Collectors.toList()),pageable,itemList::getTotalElements);
}
@Override
@Transactional
public void putItem(Long itemId, String itemName, Long itemPrice, List<ItemOptionDto> itemOptionDtos) {
Item item = itemRepository.findById(itemId)
.orElseThrow(() -> new NotExistItemException("존재하지 않는 아이템 입니다."));
item.setItemNameAndPrice(itemName,itemPrice);
itemOptionDtos.stream()
.map(itemOptionDto -> {
if(itemOptionDto.getId()==null)
return ItemOptionDto.createItemOption(itemOptionDto,item);
else
return itemOptionRepository.findById(itemOptionDto.getId())
.orElseThrow(() -> new NotExistItemException("존재하지 않는 아이템 옵션 입니다."));
})
.forEach(itemOptionRepository::save);
}
}

View File

@@ -1,19 +1,12 @@
package com.justpickup.storeservice.domain.item.web;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.justpickup.storeservice.domain.item.dto.ItemDto;
import com.justpickup.storeservice.domain.item.service.ItemService;
import com.justpickup.storeservice.domain.itemoption.dto.ItemOptionDto;
import com.justpickup.storeservice.domain.itemoption.entity.ItemOption;
import com.justpickup.storeservice.domain.itemoption.entity.OptionType;
import com.justpickup.storeservice.global.dto.Result;
import com.justpickup.storeservice.global.entity.Yn;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import org.hibernate.annotations.Parameter;
import lombok.*;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
@@ -21,7 +14,6 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.io.Serializable;
import java.util.List;
import java.util.stream.Collectors;
@@ -104,7 +96,7 @@ public class ItemController {
private String name;
private Yn salesYn;
private Long price;
private String CategoryName;
private Long CategoryId;
private List<ItemOptionResponse> itemOptions;
public GetItemResponse(ItemDto itemDto) {
@@ -112,7 +104,7 @@ public class ItemController {
this.name = itemDto.getName();
this.salesYn = itemDto.getSalesYn();
this.price = itemDto.getPrice();
this.CategoryName = itemDto.getCategoryDto().getName();
this.CategoryId = itemDto.getCategoryDto().getId();
this.itemOptions = itemDto.getItemOptions()
.stream().map(ItemOptionResponse::new)
.collect(Collectors.toList());
@@ -137,4 +129,54 @@ public class ItemController {
}
}
}
@PutMapping("/item")
public ResponseEntity<Result> putItem(@RequestBody ItemRequest putItemRequest){
List<ItemOptionDto> itemOption = putItemRequest.getRequiredOption().stream().map(ItemRequest.ItemOptionRequest::createItemDto).collect(Collectors.toList());
itemOption.addAll(putItemRequest.getOtherOption().stream().map(ItemRequest.ItemOptionRequest::createItemDto).collect(Collectors.toList()));
itemService.putItem(putItemRequest.getItemId()
, putItemRequest.getItemName()
, putItemRequest.getItemPrice()
, itemOption);
return ResponseEntity.status(HttpStatus.NO_CONTENT)
.body(Result.createSuccessResult(null));
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class ItemRequest {
private Long itemId;
private String itemName;
private Long itemPrice;
private List<ItemOptionRequest> requiredOption;
private List<ItemOptionRequest> otherOption;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public static class ItemOptionRequest {
private Long id;
private String name;
private OptionType optionType;
private Long price;
public static ItemOptionDto createItemDto(ItemOptionRequest itemOptionRequest){
return ItemOptionDto.builder()
.id(itemOptionRequest.getId())
.name(itemOptionRequest.getName())
.price(itemOptionRequest.getPrice())
.optionType(itemOptionRequest.getOptionType())
.build();
}
}
}
}

View File

@@ -1,5 +1,6 @@
package com.justpickup.storeservice.domain.itemoption.dto;
import com.justpickup.storeservice.domain.item.entity.Item;
import com.justpickup.storeservice.domain.itemoption.entity.ItemOption;
import com.justpickup.storeservice.domain.itemoption.entity.OptionType;
import lombok.AllArgsConstructor;
@@ -29,4 +30,10 @@ public class ItemOptionDto {
this.price = itemOption.getPrice();
this.name = itemOption.getName();
}
public static ItemOption createItemOption (ItemOptionDto itemOptionDto, Item item){
return new ItemOption(itemOptionDto.getOptionType(),itemOptionDto.getPrice(),itemOptionDto.getName(),item);
}
}

View File

@@ -35,4 +35,11 @@ public class ItemOption extends BaseEntity {
this.item = item;
item.getItemOptions().add(this);
}
public ItemOption(OptionType optionType, Long price, String name, Item item) {
this.optionType = optionType;
this.price = price;
this.name = name;
this.item = item;
}
}